소스 검색

dev: enable multiple selection for operators

YaoH 3 주 전
부모
커밋
ac2bbba5a5

+ 1 - 0
CMakeLists.txt

@@ -35,6 +35,7 @@ set(SOURCES
     blastopepage.cpp
     addressfactory.cpp
     addresspage.cpp
+    components/MultiSelectComboBox.cpp
     blastProject/blastprojectfactory.cpp
     blastProject/blastprojectpage.cpp
     blastProject/projectdialog.cpp

+ 56 - 26
blastProject/blastprojectpage.cpp

@@ -8,8 +8,9 @@
 #include "../global.h"
 #include "ui_blastprojectpage.h"
 
-#define BLATSTER_ROLE_ID 4
+#define BLASTER_ROLE_ID 4
 #define OPERATOR_ROLE_ID 7
+#define SAFEGUARD_ROLE_ID 5
 
 BlastProjectPage::BlastProjectPage(QWidget *parent)
     : QWidget(parent), ui(new Ui::BlastProjectPage), dao(DatabaseManager::getInstance().getDatabase()) {
@@ -22,15 +23,23 @@ BlastProjectPage::BlastProjectPage(QWidget *parent)
     pageSize = 10;
     currentPage = 1;
 
-    RefreshData();
-
     dialog = new ProjectDialog(this);
     QJsonArray pcs = backendAPIManager::getHAddresses();
     addressOptions(pcs);
-    QJsonArray sysUsersObj = backendAPIManager::getSysUsers();
-    personOptionsByType(sysUsersObj);
-    connect(dialog, &ProjectDialog::validateDetNum, this, &BlastProjectPage::insertPlan);
-    connect(dialog, &ProjectDialog::validateDetNumUpdate, this, &BlastProjectPage::updateProject);
+
+    QJsonArray allUsers = backendAPIManager::getSysUsers();
+    for (const QJsonValue &value : allUsers) {
+        if (value.isObject()) {
+            QJsonObject obj = value.toObject();
+            m_userNameById.insert(obj["identity"].toString(), obj["nickName"].toString());
+        }
+    }
+    initUserAndDialogOptions(allUsers);
+
+    connect(dialog, &ProjectDialog::createProject, this, &BlastProjectPage::createProject);
+    connect(dialog, &ProjectDialog::saveProject, this, &BlastProjectPage::updateProject);
+
+    RefreshData();
 }
 
 void BlastProjectPage::RefreshData() {
@@ -50,10 +59,15 @@ void BlastProjectPage::loadDataFromSource(int currentPage, int pageSize) {
 
     // 定义表头信息
     QList<HeaderInfo> headers = {
-        {"工程名称", "name"},          {"井上爆破员", "operatorName"},
-        {"井下爆破员", "blasterName"}, {"井下地址", "addressPath"},
-        {"雷管数量", "detSum"},        {"起爆器数量", "blastCount"},
-        {"起爆状态", "blastStatus"},   {"操作", ""},
+        {"工程名称", "name"},
+        {"安全员", "safetyInspectorIdentity"},
+        {"井上爆破员", "operatorIdentity"},
+        {"井下爆破员", "blasterIdentity"},
+        {"井下地址", "addressPath"},
+        {"雷管数量", "detSum"},
+        {"起爆器数量", "blastCount"},
+        {"起爆状态", "blastStatus"},
+        {"操作", ""},
     };
 
     int headerCount = headers.size();
@@ -66,15 +80,15 @@ void BlastProjectPage::loadDataFromSource(int currentPage, int pageSize) {
     model->setHorizontalHeaderLabels(headerLabels);
 
     for (int row = 0; row < projectList.size(); ++row) {
-        HProject &HProject = *projectList.at(row).data();
+        HProject &hProject = *projectList.at(row).data();
         for (int col = 0; col < headers.size(); ++col) {
             QString prop = propMap[col];
             QStandardItem *item = nullptr;
 
             if (!prop.isEmpty()) {
                 QMetaProperty metaProp =
-                    HProject.metaObject()->property(HProject.metaObject()->indexOfProperty(prop.toUtf8()));
-                QVariant value = metaProp.read(&HProject);
+                    hProject.metaObject()->property(hProject.metaObject()->indexOfProperty(prop.toUtf8()));
+                QVariant value = metaProp.read(&hProject);
                 if (prop == "blastStatus") {
                     QString statusText;
                     if (value.toString() == BlastStatus::Created) {
@@ -97,6 +111,10 @@ void BlastProjectPage::loadDataFromSource(int currentPage, int pageSize) {
                     } else {
                         item = new QStandardItem(value.toString());
                     }
+                } else if (QStringList({"safetyInspectorIdentity", "operatorIdentity", "blasterIdentity"})
+                               .contains(prop)) {
+                    qDebug() << "prop" << prop << value.toString().split(",");
+                    item = new QStandardItem(getUserNameByIds(value.toString().split(",")));
                 } else {
                     item = new QStandardItem(value.toString());
                 }
@@ -145,27 +163,32 @@ void BlastProjectPage::onComboBoxIndexChanged(int index) {
     loadDataFromSource(currentPage, pageSize);
 }
 
-void BlastProjectPage::personOptionsByType(const QJsonArray &dataArray) {
+void BlastProjectPage::initUserAndDialogOptions(const QJsonArray &dataArray) {
     QJsonArray blasterDeployerArray;
     QJsonArray operatorArray;
+    QJsonArray safeGuardArray;
     for (const QJsonValue &value : dataArray) {
         if (value.isObject()) {
             QJsonObject obj = value.toObject();
             if (obj.contains("roleIds")) {
                 QJsonArray roleIdsArray = obj["roleIds"].toArray();
                 // Note: 一个用户可能存在多个角色
-                if (roleIdsArray.contains(BLATSTER_ROLE_ID)) {  // 井下爆破员
+                if (roleIdsArray.contains(BLASTER_ROLE_ID)) {  // 井下爆破员
                     blasterDeployerArray.append(obj);
                 }
                 if (roleIdsArray.contains(OPERATOR_ROLE_ID)) {  // 井上爆破员
                     operatorArray.append(obj);
                 }
+                if (roleIdsArray.contains(SAFEGUARD_ROLE_ID)) {  // 安全员
+                    safeGuardArray.append(obj);
+                }
             }
         }
     }
 
-    dialog->SetComboBoxBlast(blasterDeployerArray);
-    dialog->SetComboBoxOperator(operatorArray);
+    dialog->initComboBoxBlaster(blasterDeployerArray);
+    dialog->initComboBoxOperator(operatorArray);
+    dialog->initComboBoxSafetyInspectors(safeGuardArray);
 }
 
 void BlastProjectPage::addressOptions(const QJsonArray &dataArrayAddress) {
@@ -173,7 +196,6 @@ void BlastProjectPage::addressOptions(const QJsonArray &dataArrayAddress) {
     // dialog->setChildOptions(dataArrayAddress);
 }
 
-// 递归函数,用于提取所有的 name 信bian'ji
 void BlastProjectPage::extractNames(const QJsonArray &array, QStringList &names) {
     for (const auto &item : array) {
         if (item.isObject()) {
@@ -197,21 +219,21 @@ void BlastProjectPage::parseJsonString(const QJsonDocument &jsonDoc) {
 void BlastProjectPage::on_pushButton_clicked() {
     dialog->setModal(false);
     dialog->clearFormData();
-    dialog->setOperationStatus(0);
+    dialog->setOperationType(0);
     int ref = dialog->exec();
     if (ref == QDialog::Accepted) {};
 }
 
-void BlastProjectPage::insertPlan(const QMap<QString, QString> &data) {
+void BlastProjectPage::createProject(const QMap<QString, QString> &data) {
     HProject project;
+    qDebug() << "xxx: " << data.value("safetyInspectorIdentity");
     project.setName(data.value("name"));
     project.setDetSum(data.value("detNum"));
-    project.setBlasterName(data.value("blasterName"));
-    project.setOperatorName(data.value("operatorName"));
     project.setAddressUuid(data.value("addressUuid"));
     project.setAddressPath(data.value("addressPath"));
     project.setBlasterIdentity(data.value("blasterIdentity"));
     project.setOperatorIdentity(data.value("operatorIdentity"));
+    project.setSafetyInspectorIdentity(data.value("safetyInspectorIdentity"));
     project.setLevel4Address(data.value("level4Address"));
     project.setBlastCount(data.value("blastCount"));
     project.setBlastStatus("1");
@@ -245,7 +267,7 @@ void BlastProjectPage::publishAllPendingBlastProjects() {
 
 void BlastProjectPage::updateButtonClicked(const HProject &project) {
     dialog->setModal(false);
-    dialog->setOperationStatus(1);
+    dialog->setOperationType(1);
     dialog->setFormData(project);
     int ref = dialog->exec();
     if (ref == QDialog::Accepted) {};
@@ -256,8 +278,6 @@ void BlastProjectPage::updateProject(const QMap<QString, QString> &data) {
     project.setId(updateId);
     project.setName(data.value("name"));
     project.setDetSum(data.value("detNum"));
-    project.setBlasterName(data.value("blasterName"));
-    project.setOperatorName(data.value("operatorName"));
     project.setAddressUuid(data.value("addressUuid"));
     project.setBlasterIdentity(data.value("blasterIdentity"));
     project.setOperatorIdentity(data.value("operatorIdentity"));
@@ -295,4 +315,14 @@ void BlastProjectPage::messageAndTopicReceived(const QByteArray &message, const
 
 void BlastProjectPage::updateProjectUUId(QString uuid) { dao.updateBlastStatusByUuid(uuid, "2"); }
 
+QString BlastProjectPage::getUserNameByIds(const QStringList &userIds) {
+    QStringList userNames;
+    for (const QString &userId : userIds) {
+        if (m_userNameById.contains(userId)) {
+            userNames.append(m_userNameById[userId]);
+        }
+    }
+    return userNames.join(",");
+}
+
 BlastProjectPage::~BlastProjectPage() { delete ui; }

+ 4 - 2
blastProject/blastprojectpage.h

@@ -34,11 +34,11 @@ class BlastProjectPage : public QWidget {
    private:
     void RefreshData();
     void loadDataFromSource(int currentPage, int pageSize);
-    void personOptionsByType(const QJsonArray &arrayData);
+    void initUserAndDialogOptions(const QJsonArray &arrayData);
     void addressOptions(const QJsonArray &jsonArray);
     void parseJsonString(const QJsonDocument &jsonDoc);
     void fillComboBox(QComboBox *comboBox, const QJsonArray &jsonArray);
-    void insertPlan(const QMap<QString, QString> &data);
+    void createProject(const QMap<QString, QString> &data);
     void updateButtonClicked(const HProject &project);
     void updateProject(const QMap<QString, QString> &data);
     void deleteButtonClicked(const HProject &project);
@@ -53,10 +53,12 @@ class BlastProjectPage : public QWidget {
     HProjectDao dao;
     QJsonArray dataArray;
     QJsonArray dataArrayAddress;
+    QMap<QString, QString> m_userNameById;
     int pageSize;     // 每页显示的记录数量
     int currentPage;  // 当前页面
     int totalCount;
     int updateId;
+    QString getUserNameByIds(const QStringList &userIds);
 };
 
 #endif  // BLASTPROJECTPAGE_H

+ 5 - 8
blastProject/hproject.cpp

@@ -27,17 +27,15 @@ QString HProject::getXmbh() const { return xmbh; }
 
 void HProject::setXmbh(const QString &newXmbh) { xmbh = newXmbh; }
 
-QString HProject::getOperatorName() const { return operatorName; }
-
-void HProject::setOperatorName(const QString &newOperatorName) { operatorName = newOperatorName; }
-
 QString HProject::getOperatorIdentity() const { return operatorIdentity; }
 
 void HProject::setOperatorIdentity(const QString &newOperatorIdentity) { operatorIdentity = newOperatorIdentity; }
 
-QString HProject::getBlasterName() const { return blasterName; }
+QString HProject::getSafetyInspectorIdentity() const { return safetyInspectorIdentity; }
 
-void HProject::setBlasterName(const QString &newBlasterName) { blasterName = newBlasterName; }
+void HProject::setSafetyInspectorIdentity(const QString &newSafetyInspectorIdentity) {
+    safetyInspectorIdentity = newSafetyInspectorIdentity;
+}
 
 QString HProject::getBlasterIdentity() const { return blasterIdentity; }
 
@@ -107,11 +105,10 @@ QByteArray HProject::ProjectToJson(const HProject &project) {
     jsonObject["htid"] = project.getHtid();
     jsonObject["companyCode"] = project.getCompanyCode();
     jsonObject["detSum"] = project.getDetSum();
-    jsonObject["blasterName"] = project.getBlasterName();
-    jsonObject["operatorName"] = project.getOperatorName();
     jsonObject["address"] = project.getAddressUuid();
     jsonObject["blasterIdentity"] = project.getBlasterIdentity();
     jsonObject["operatorIdentity"] = project.getOperatorIdentity();
+    jsonObject["safetyInspectorIdentity"] = project.getSafetyInspectorIdentity();
     jsonObject["level4Address"] = project.getLevel4Address();
     jsonObject["blastCount"] = project.getBlastCount();
     QJsonDocument jsonDoc(jsonObject);

+ 5 - 10
blastProject/hproject.h

@@ -22,9 +22,8 @@ class HProject : public QObject {
     Q_PROPERTY(QString companyCode READ getCompanyCode WRITE setCompanyCode)
     Q_PROPERTY(QString htid READ getHtid WRITE setHtid)
     Q_PROPERTY(QString xmbh READ getXmbh WRITE setXmbh)
-    Q_PROPERTY(QString operatorName READ getOperatorName WRITE setOperatorName)
     Q_PROPERTY(QString operatorIdentity READ getOperatorIdentity WRITE setOperatorIdentity)
-    Q_PROPERTY(QString blasterName READ getBlasterName WRITE setBlasterName)
+    Q_PROPERTY(QString safetyInspectorIdentity READ getSafetyInspectorIdentity WRITE setSafetyInspectorIdentity)
     Q_PROPERTY(QString blasterIdentity READ getBlasterIdentity WRITE setBlasterIdentity)
     Q_PROPERTY(QString addressUuid READ getAddressUuid WRITE setAddressUuid)
     Q_PROPERTY(QString addressPath READ getAddressPath WRITE setAddressPath)
@@ -63,15 +62,9 @@ class HProject : public QObject {
     QString getXmbh() const;
     void setXmbh(const QString &newXmbh);
 
-    QString getOperatorName() const;
-    void setOperatorName(const QString &newOperatorName);
-
     QString getOperatorIdentity() const;
     void setOperatorIdentity(const QString &newOperatorIdentity);
 
-    QString getBlasterName() const;
-    void setBlasterName(const QString &newBlasterName);
-
     QString getBlasterIdentity() const;
     void setBlasterIdentity(const QString &newBlasterIdentity);
 
@@ -119,6 +112,9 @@ class HProject : public QObject {
     QString getLevel4Address() const;
     void setLevel4Address(const QString &newLoraSn);
 
+    QString getSafetyInspectorIdentity() const;
+    void setSafetyInspectorIdentity(const QString &newSafetyInspectorIdentity);
+
    private:
     qint64 id;
     QString uuid;
@@ -126,9 +122,8 @@ class HProject : public QObject {
     QString companyCode;
     QString htid;
     QString xmbh;
-    QString operatorName;
     QString operatorIdentity;
-    QString blasterName;
+    QString safetyInspectorIdentity;
     QString blasterIdentity;
     QString addressUuid;
     QString addressPath;

+ 21 - 27
blastProject/hprojectdao.cpp

@@ -7,26 +7,22 @@ HProjectDao::HProjectDao(QSqlDatabase db) : database(db) {}
 bool HProjectDao::addHProject(const HProject &project) {
     QSqlQuery query;
     query.prepare(
-        "INSERT INTO h_project (uuid, name, company_code, htid, xmbh, operator_name, "
-        "operator_identity, blaster_name, blaster_identity, address_uuid, address_path, "
-        "level4_address,pc_sn, "
-        "det_sum, file_name, file_url, blast_status,blast_count,created_at, updated_at, "
+        "INSERT INTO h_project (uuid, name, company_code, htid, xmbh,  "
+        "operator_identity, blaster_identity, safety_inspector_identity, address_uuid, address_path, "
+        "level4_address,pc_sn,det_sum, file_name, file_url, blast_status,blast_count,created_at, updated_at, "
         "deleted_at, create_by, update_by) "
-        "VALUES (:uuid, :name, :companyCode, :htid, :xmbh, :operatorName, :operatorIdentity, "
-        ":blasterName, :blasterIdentity, :addressUuid, :addressPath, :level4Address, :pcSn, :detSum, "
-        ":fileName, "
-        ":fileUrl, :blastStatus, :blastCount,:createdAt, :updatedAt, :deletedAt, :createBy, "
-        ":updateBy)");
+        "VALUES (:uuid, :name, :companyCode, :htid, :xmbh, :operatorIdentity, "
+        ":blasterIdentity, :safetyInspectorIdentity, :addressUuid, :addressPath, :level4Address, :pcSn, :detSum, "
+        ":fileName, :fileUrl, :blastStatus, :blastCount,:createdAt, :updatedAt, :deletedAt, :createBy, :updateBy)");
 
     query.bindValue(":uuid", project.getUuid());
     query.bindValue(":name", project.getName());
     query.bindValue(":companyCode", project.getCompanyCode());
     query.bindValue(":htid", project.getHtid());
     query.bindValue(":xmbh", project.getXmbh());
-    query.bindValue(":operatorName", project.getOperatorName());
     query.bindValue(":operatorIdentity", project.getOperatorIdentity());
-    query.bindValue(":blasterName", project.getBlasterName());
     query.bindValue(":blasterIdentity", project.getBlasterIdentity());
+    query.bindValue(":safetyInspectorIdentity", project.getSafetyInspectorIdentity());
     query.bindValue(":addressUuid", project.getAddressUuid());
     query.bindValue(":addressPath", project.getAddressPath());
     query.bindValue(":level4Address", project.getLevel4Address());
@@ -42,6 +38,7 @@ bool HProjectDao::addHProject(const HProject &project) {
     query.bindValue(":createBy", project.getCreateBy());
     query.bindValue(":updateBy", project.getUpdateBy());
     if (!query.exec()) {
+        qDebug() << "HProjectDao::addHProject query: " << query.lastQuery() << query.lastError().text();
         return false;
     }
 
@@ -57,10 +54,9 @@ bool HProjectDao::updateHProject(const HProject &project) {
         "company_code = :companyCode, "
         "htid = :htid, "
         "xmbh = :xmbh, "
-        "operator_name = :operatorName, "
         "operator_identity = :operatorIdentity, "
-        "blaster_name = :blasterName, "
         "blaster_identity = :blasterIdentity, "
+        "safetyInspector_identity = :safetyInspectorIdentity, "
         "address_uuid = :addressUuid, "
         "address_path = :addressPath, "
         "pc_sn = :pcSn, "
@@ -81,10 +77,9 @@ bool HProjectDao::updateHProject(const HProject &project) {
     query.bindValue(":companyCode", project.getCompanyCode());
     query.bindValue(":htid", project.getHtid());
     query.bindValue(":xmbh", project.getXmbh());
-    query.bindValue(":operatorName", project.getOperatorName());
     query.bindValue(":operatorIdentity", project.getOperatorIdentity());
-    query.bindValue(":blasterName", project.getBlasterName());
     query.bindValue(":blasterIdentity", project.getBlasterIdentity());
+    query.bindValue(":safetyInspectorIdentity", project.getSafetyInspectorIdentity());
     query.bindValue(":addressUuid", project.getAddressUuid());
     query.bindValue(":addressPath", project.getAddressPath());
     query.bindValue(":pcSn", project.getPcSn());
@@ -107,7 +102,7 @@ bool HProjectDao::updateHProject(const HProject &project) {
 
 bool HProjectDao::deleteHProject(const HProject &project) {
     QSqlQuery query;
-    query.prepare("update h_project set deletion_date = :deletionDate WHERE id = :id");
+    query.prepare("update h_project set deleted_at = :deletionDate WHERE id = :id");
     query.bindValue(":id", project.getId());
     query.bindValue(":deletionDate", QDateTime::currentDateTime());
     if (!query.exec()) {
@@ -132,7 +127,7 @@ PaginatedHProjectResult HProjectDao::getAllHProjects(int page, int pageSize) {
     QSqlQuery query(database);
     int offset = (page - 1) * pageSize;
     query.prepare(
-        "SELECT * FROM h_project WHERE deletion_date IS NULL ORDER BY created_at desc LIMIT :pageSize OFFSET :offset");
+        "SELECT * FROM h_project WHERE deleted_at IS NULL ORDER BY created_at desc LIMIT :pageSize OFFSET :offset");
     query.bindValue(":pageSize", pageSize);
     query.bindValue(":offset", offset);
 
@@ -144,7 +139,7 @@ PaginatedHProjectResult HProjectDao::getAllHProjects(int page, int pageSize) {
         qWarning() << "Query execution failed: " << query.lastError().text();
     }
 
-    query.prepare("SELECT COUNT(*) FROM h_project");
+    query.prepare("SELECT COUNT(*) FROM h_project where deleted_at IS NULL");
     int totalCount = 0;
     if (query.exec() && query.next()) {
         totalCount = query.value(0).toInt();
@@ -157,7 +152,7 @@ PaginatedHProjectResult HProjectDao::getAllHProjectsByOpera(int page, int pageSi
     QSqlQuery query(database);
     int offset = (page - 1) * pageSize;
     query.prepare(
-        "SELECT * FROM h_project WHERE blast_status = :safeCheckedStatus and deletion_date IS NULL ORDER BY created_at "
+        "SELECT * FROM h_project WHERE blast_status = :safeCheckedStatus and deleted_at IS NULL ORDER BY created_at "
         "desc "
         "LIMIT :pageSize OFFSET :offset");
     query.bindValue(":safeCheckedStatus", BlastStatus::SafeChecked);
@@ -171,7 +166,8 @@ PaginatedHProjectResult HProjectDao::getAllHProjectsByOpera(int page, int pageSi
         qWarning() << "Query execution failed: " << query.lastError().text();
     }
 
-    query.prepare("SELECT COUNT(*) FROM h_project WHERE blast_status > 1");
+    query.prepare("SELECT COUNT(*) FROM h_project WHERE blast_status = :safeCheckedStatus and deleted_at IS NULL");
+    query.bindValue(":safeCheckedStatus", BlastStatus::SafeChecked);
     int totalCount = 0;
     if (query.exec() && query.next()) {
         totalCount = query.value(0).toInt();
@@ -197,7 +193,7 @@ QList<QSharedPointer<HProject>> HProjectDao::getRegistedProjectByAddressUuid(QLi
 
     QString queryStr = QString(
                            "SELECT * FROM h_project WHERE address_uuid IN (%1) "
-                           "AND blast_status = %2 ORDER BY created_at")
+                           "AND blast_status = %2 and deleted_at is NULL ORDER BY created_at")
                            .arg(placeholders)
                            .arg(BlastStatus::Registered);
 
@@ -223,9 +219,8 @@ QList<QSharedPointer<HProject>> HProjectDao::getRegistedProjectByAddressUuid(QLi
 QList<QSharedPointer<HProject>> HProjectDao::getAllHProjectsReg() {
     QList<QSharedPointer<HProject>> projects;
     QSqlQuery query(database);
-    query.prepare(
-        QString("SELECT * FROM h_project WHERE blast_status = %1 and deletion_date is null ORDER BY created_at ")
-            .arg(BlastStatus::Created));
+    query.prepare(QString("SELECT * FROM h_project WHERE blast_status = %1 and deleted_at is null ORDER BY created_at ")
+                      .arg(BlastStatus::Created));
     // 执行查询并打印SQL
     if (query.exec()) {
         while (query.next()) {
@@ -243,7 +238,7 @@ bool HProjectDao::updateBlastStatusByUuid(const QString &uuid, const QString &bl
     query.prepare(
         "UPDATE h_project SET "
         "blast_status = :blastStatus "
-        "WHERE uuid = :uuid and blast_status != :blastStatus");
+        "WHERE uuid = :uuid and blast_status != :blastStatus and deleted_at is NULL;");
     query.bindValue(":uuid", uuid);
     query.bindValue(":blastStatus", blastStatus);
 
@@ -262,10 +257,9 @@ QSharedPointer<HProject> HProjectDao::recordToProject(const QSqlRecord &record)
     project->setCompanyCode(record.value("company_code").toString());
     project->setHtid(record.value("htid").toString());
     project->setXmbh(record.value("xmbh").toString());
-    project->setOperatorName(record.value("operator_name").toString());
     project->setOperatorIdentity(record.value("operator_identity").toString());
-    project->setBlasterName(record.value("blaster_name").toString());
     project->setBlasterIdentity(record.value("blaster_identity").toString());
+    project->setSafetyInspectorIdentity(record.value("safety_inspector_identity").toString());
     project->setAddressUuid(record.value("address_uuid").toString());
     project->setAddressPath(record.value("address_path").toString());
     project->setPcSn(record.value("pc_sn").toString());

+ 56 - 173
blastProject/projectdialog.cpp

@@ -1,10 +1,13 @@
 #include "projectdialog.h"
 
+#include <QtCore/qlogging.h>
+
 #include <QCheckBox>
 #include <QListView>
 #include <QPushButton>
 #include <QStandardItemModel>
 
+#include "../components/MultiSelectComboBox.h"
 #include "ui_projectdialog.h"
 
 ProjectDialog::ProjectDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ProjectDialog) {
@@ -13,80 +16,52 @@ ProjectDialog::ProjectDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Proj
     connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &ProjectDialog::clearFormData);
 }
 
-void ProjectDialog::SetComboBoxBlast(const QJsonArray &bapoYuanArray) {
-    blasterArray = bapoYuanArray;
-
-    // 创建标准项模型用于多选
-    QStandardItemModel *blastModel = new QStandardItemModel(ui->comboBoxBlast);
+void ProjectDialog::initComboBoxBlaster(const QJsonArray &undergroundOperators) {
+    MultiSelectComboBox *comboBox = new MultiSelectComboBox(this);
 
-    for (const QJsonValue &value : bapoYuanArray) {
+    for (const QJsonValue &value : undergroundOperators) {
         if (value.isObject()) {
             QJsonObject obj = value.toObject();
             if (obj.contains("nickName")) {
                 QString name = obj["nickName"].toString();
-                QStandardItem *item = new QStandardItem(name);
-                item->setCheckable(true);
-                item->setData(obj["identity"].toString(), Qt::UserRole);
-                blastModel->appendRow(item);
+                comboBox->addItem(obj["identity"].toString(), name);
             }
         }
     }
-
-    ui->comboBoxBlast->setModel(blastModel);
-    ui->comboBoxBlast->setView(new QListView(ui->comboBoxBlast));
-    ui->comboBoxBlast->view()->setMinimumWidth(200);
-
-    // 连接项变更信号
-    connect(blastModel, &QStandardItemModel::itemChanged, this, &ProjectDialog::onBlasterItemChanged);
+    QVBoxLayout *layout = new QVBoxLayout(ui->undergroundOperatorsWidget);
+    layout->addWidget(comboBox);
 }
 
-void ProjectDialog::SetComboBoxOperator(const QJsonArray &anQuanYuanArray) {
-    operatorArray = anQuanYuanArray;
+void ProjectDialog::initComboBoxSafetyInspectors(const QJsonArray &safeInspectors) {
+    MultiSelectComboBox *comboBox = new MultiSelectComboBox(this);
 
-    // 创建标准项模型用于多选
-    QStandardItemModel *operatorModel = new QStandardItemModel(ui->comboBoxOperator);
-
-    for (const QJsonValue &value : anQuanYuanArray) {
+    for (const QJsonValue &value : safeInspectors) {
         if (value.isObject()) {
             QJsonObject obj = value.toObject();
             if (obj.contains("nickName")) {
                 QString name = obj["nickName"].toString();
-                QStandardItem *item = new QStandardItem(name);
-                item->setCheckable(true);
-                item->setData(obj["identity"].toString(), Qt::UserRole);
-                operatorModel->appendRow(item);
+                comboBox->addItem(obj["identity"].toString(), name);
             }
         }
     }
-
-    ui->comboBoxOperator->setModel(operatorModel);
-    ui->comboBoxOperator->setView(new QListView(ui->comboBoxOperator));
-    ui->comboBoxOperator->view()->setMinimumWidth(200);
-
-    // 连接项变更信号
-    connect(operatorModel, &QStandardItemModel::itemChanged, this, &ProjectDialog::onOperatorItemChanged);
+    QVBoxLayout *layout = new QVBoxLayout(ui->safetyInspectorsWidget);
+    layout->addWidget(comboBox);
 }
 
-void ProjectDialog::on_comboBoxOperator_currentIndexChanged(int index) {
-    // 此槽函数保留以避免编译错误,但不再需要之前的功能
-    // 多选框的变更由 onOperatorItemChanged 处理
-}
-
-void ProjectDialog::on_comboBoxBlast_currentIndexChanged(int index) {
-    // 此槽函数保留以避免编译错误,但不再需要之前的功能
-    // 多选框的变更由 onBlasterItemChanged 处理
-}
+void ProjectDialog::initComboBoxOperator(const QJsonArray &groundOperators) {
+    MultiSelectComboBox *comboBox = new MultiSelectComboBox(this);
 
-void ProjectDialog::fillComboBox(QComboBox *comboBox, const QJsonArray &jsonArray) {
-    for (const QJsonValue &value : jsonArray) {
+    for (const QJsonValue &value : groundOperators) {
         if (value.isObject()) {
             QJsonObject obj = value.toObject();
             if (obj.contains("nickName")) {
                 QString name = obj["nickName"].toString();
-                comboBox->addItem(name);
+                comboBox->addItem(obj["identity"].toString(), name);
             }
         }
     }
+    QVBoxLayout *layout = new QVBoxLayout(ui->groundOperatorsWidget);
+    layout->addWidget(comboBox);
 }
 
 void ProjectDialog::SetL1AddressOptions(const QJsonArray &Options) {
@@ -136,35 +111,10 @@ void ProjectDialog::clearFormData() {
     for (QLineEdit *lineEdit : lineEdits) {
         lineEdit->clear();
     }
-
-    // 清除多选ComboBox
-    if (ui->comboBoxBlast->model()) {
-        QStandardItemModel *blastModel = qobject_cast<QStandardItemModel *>(ui->comboBoxBlast->model());
-        if (blastModel) {
-            for (int i = 0; i < blastModel->rowCount(); ++i) {
-                QStandardItem *item = blastModel->item(i);
-                if (item) {
-                    item->setCheckState(Qt::Unchecked);
-                }
-            }
-        }
-        ui->comboBoxBlast->setEditText("请选择井下爆破员");
-    }
-
-    if (ui->comboBoxOperator->model()) {
-        QStandardItemModel *operatorModel = qobject_cast<QStandardItemModel *>(ui->comboBoxOperator->model());
-        if (operatorModel) {
-            for (int i = 0; i < operatorModel->rowCount(); ++i) {
-                QStandardItem *item = operatorModel->item(i);
-                if (item) {
-                    item->setCheckState(Qt::Unchecked);
-                }
-            }
-        }
-        ui->comboBoxOperator->setEditText("请选择井上爆破员");
+    QList<MultiSelectComboBox *> multiSelectCombos = findChildren<MultiSelectComboBox *>();
+    for (MultiSelectComboBox *combo : multiSelectCombos) {
+        combo->setCheckedKeys(QStringList());
     }
-
-    // 清除普通ComboBox
     ui->comboBoxAddr->setCurrentIndex(-1);
     ui->comboBoxAddr_2->setCurrentIndex(-1);
     ui->comboBoxAddr_3->setCurrentIndex(-1);
@@ -172,8 +122,7 @@ void ProjectDialog::clearFormData() {
     // 清除选中数据
     selectedBlasters.clear();
     selectedOperators.clear();
-    blasterIds.clear();
-    operatorIds.clear();
+    selectedSafetyInspectors.clear();  // Add this line
 
     // 显示确认按钮
     ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(true);
@@ -195,6 +144,21 @@ void ProjectDialog::on_comboBoxAddr_2_currentIndexChanged(int index) {
 }
 
 void ProjectDialog::validateAndSaveProject() {
+    for (const auto &pair :
+         ui->undergroundOperatorsWidget->findChild<MultiSelectComboBox *>()->checkedItemsWithKeys()) {
+        selectedBlasters.append(pair.first);
+        qDebug() << "Selected underground operator:" << pair.first;
+    }
+    for (const auto &pair : ui->groundOperatorsWidget->findChild<MultiSelectComboBox *>()->checkedItemsWithKeys()) {
+        selectedOperators.append(pair.first);
+        qDebug() << "Selected ground operator:" << pair.first;
+    }
+    for (const auto &pair : ui->safetyInspectorsWidget->findChild<MultiSelectComboBox *>()->checkedItemsWithKeys()) {
+        selectedSafetyInspectors.append(pair.first);
+
+        qDebug() << "Selected safety inspector:" << pair.first;
+    }
+
     QString detNum = ui->editDetNum->text().trimmed();
     QString blastCount = ui->editRegCount->text().trimmed();
     QString projectName = ui->editName->text().trimmed();
@@ -205,16 +169,18 @@ void ProjectDialog::validateAndSaveProject() {
         QMessageBox::warning(nullptr, "输入错误", "请输入0-10000的数字!");
         return;
     }
-
     if (selectedBlasters.isEmpty()) {
         QMessageBox::warning(nullptr, "选择错误", "请至少选择一名井下爆破员!");
         return;
     }
-
     if (selectedOperators.isEmpty()) {
         QMessageBox::warning(nullptr, "选择错误", "请至少选择一名井上爆破员!");
         return;
     }
+    if (selectedSafetyInspectors.isEmpty()) {
+        QMessageBox::warning(nullptr, "选择错误", "请至少选择一名安全员!");
+        return;
+    }
 
     // 创建一个 QMap 集合,存储数据
     QMap<QString, QString> data;
@@ -222,8 +188,6 @@ void ProjectDialog::validateAndSaveProject() {
     data["name"] = projectName;
     data["xmbh"] = xmbh;
     data["htid"] = htid;
-    data["operatorName"] = selectedOperators.join(", ");
-    data["blasterName"] = selectedBlasters.join(", ");
 
     // 选择level最低的uuid
     QString addressUuid = ui->comboBoxAddr->currentData().toString().trimmed();
@@ -244,22 +208,25 @@ void ProjectDialog::validateAndSaveProject() {
 
     data["addressPath"] = l1AddressName + "/" + l2AddressName + "/" + l3AddressName;
     data["addressUuid"] = addressUuid;
-    data["blasterIdentity"] = blasterIds.join(",");
-    data["operatorIdentity"] = operatorIds.join(",");
+    data["blasterIdentity"] = selectedBlasters.join(",");
+    data["operatorIdentity"] = selectedOperators.join(",");
+    data["safetyInspectorIdentity"] = selectedSafetyInspectors.join(",");
     data["blastCount"] = blastCount;
 
-    if (operationStatus == 0) {
-        emit validateDetNum(data);
-    } else if (operationStatus == 1) {
-        emit validateDetNumUpdate(data);
+    if (operationType == 0) {
+        qDebug() << "Project data to create:" << data;
+        emit createProject(data);
+    } else if (operationType == 1) {
+        qDebug() << "Project data to update:" << data;
+        emit saveProject(data);
     }
 
     clearFormData();  // 清除表单数据
 }
 
-int ProjectDialog::getOperationStatus() const { return operationStatus; }
+int ProjectDialog::getOperationType() const { return operationType; }
 
-void ProjectDialog::setOperationStatus(int newOperationStatus) { operationStatus = newOperationStatus; }
+void ProjectDialog::setOperationType(int newOperationType) { operationType = newOperationType; }
 
 void ProjectDialog::setFormData(const HProject &Project) {
     try {
@@ -274,41 +241,7 @@ void ProjectDialog::setFormData(const HProject &Project) {
         ui->editRegCount->setText(Project.getBlastCount());
         ui->level4Address->setText(Project.getLevel4Address());
 
-        // 设置井下爆破员多选
-        QStringList blasterNames = Project.getBlasterName().split(", ");
-        QStringList blasterIdList = Project.getBlasterIdentity().split(",");
-        QStandardItemModel *blastModel = qobject_cast<QStandardItemModel *>(ui->comboBoxBlast->model());
-        if (blastModel) {
-            for (int i = 0; i < blastModel->rowCount(); ++i) {
-                QStandardItem *item = blastModel->item(i);
-                if (item && blasterNames.contains(item->text())) {
-                    item->setCheckState(Qt::Checked);
-                }
-            }
-        }
-
-        // 设置井上爆破员多选
-        QStringList operatorNames = Project.getOperatorName().split(", ");
-        QStringList operatorIdList = Project.getOperatorIdentity().split(",");
-        QStandardItemModel *operatorModel = qobject_cast<QStandardItemModel *>(ui->comboBoxOperator->model());
-        if (operatorModel) {
-            for (int i = 0; i < operatorModel->rowCount(); ++i) {
-                QStandardItem *item = operatorModel->item(i);
-                if (item && operatorNames.contains(item->text())) {
-                    item->setCheckState(Qt::Checked);
-                }
-            }
-        }
-
-        // 更新已选择列表
-        selectedBlasters = blasterNames;
-        blasterIds = blasterIdList;
-        selectedOperators = operatorNames;
-        operatorIds = operatorIdList;
-
-        // 更新ComboBox显示文本
-        ui->comboBoxBlast->setEditText(selectedBlasters.join(", "));
-        ui->comboBoxOperator->setEditText(selectedOperators.join(", "));
+        // TODO: 这里需要处理井下爆破员和井上爆破员及安全员
 
         // 设置地址选择
         QStringList addressParts = Project.getAddressPath().split("/");
@@ -360,54 +293,4 @@ void ProjectDialog::setFormData(const HProject &Project) {
     }
 }
 
-void ProjectDialog::onBlasterItemChanged(QStandardItem *item) { updateSelectedBlasters(); }
-
-void ProjectDialog::onOperatorItemChanged(QStandardItem *item) { updateSelectedOperators(); }
-
-void ProjectDialog::updateSelectedBlasters() {
-    selectedBlasters.clear();
-    blasterIds.clear();
-    QStandardItemModel *model = qobject_cast<QStandardItemModel *>(ui->comboBoxBlast->model());
-
-    if (!model) return;
-
-    for (int i = 0; i < model->rowCount(); ++i) {
-        QStandardItem *item = model->item(i);
-        if (item && item->checkState() == Qt::Checked) {
-            selectedBlasters.append(item->text());
-            blasterIds.append(item->data(Qt::UserRole).toString());
-        }
-    }
-
-    // 更新显示的文本
-    if (selectedBlasters.isEmpty()) {
-        ui->comboBoxBlast->setEditText("请选择井下爆破员");
-    } else {
-        ui->comboBoxBlast->setEditText(selectedBlasters.join(", "));
-    }
-}
-
-void ProjectDialog::updateSelectedOperators() {
-    selectedOperators.clear();
-    operatorIds.clear();
-    QStandardItemModel *model = qobject_cast<QStandardItemModel *>(ui->comboBoxOperator->model());
-
-    if (!model) return;
-
-    for (int i = 0; i < model->rowCount(); ++i) {
-        QStandardItem *item = model->item(i);
-        if (item && item->checkState() == Qt::Checked) {
-            selectedOperators.append(item->text());
-            operatorIds.append(item->data(Qt::UserRole).toString());
-        }
-    }
-
-    // 更新显示的文本
-    if (selectedOperators.isEmpty()) {
-        ui->comboBoxOperator->setEditText("请选择井上爆破员");
-    } else {
-        ui->comboBoxOperator->setEditText(selectedOperators.join(", "));
-    }
-}
-
 ProjectDialog::~ProjectDialog() { delete ui; }

+ 14 - 25
blastProject/projectdialog.h

@@ -1,14 +1,13 @@
 #ifndef PROJECTDIALOG_H
 #define PROJECTDIALOG_H
 
-#include <QComboBox>
 #include <QDialog>
 #include <QHash>
 #include <QJsonArray>
 #include <QJsonObject>
 #include <QStandardItem>
-#include <QStringList>
 
+#include "../components/MultiSelectComboBox.h"
 #include "../regex.h"
 #include "hproject.h"
 
@@ -22,42 +21,39 @@ class ProjectDialog : public QDialog {
    public:
     explicit ProjectDialog(QWidget *parent = nullptr);
     ~ProjectDialog();
-    void SetComboBoxBlast(const QJsonArray &bapoYuan);
-    void SetComboBoxOperator(const QJsonArray &anQuanYuanArray);
+    void initComboBoxBlaster(const QJsonArray &bapoYuan);
+    void initComboBoxOperator(const QJsonArray &anQuanYuanArray);
+    void initComboBoxSafetyInspectors(const QJsonArray &safeGuardsArray);
     void SetL1AddressOptions(const QJsonArray &parentOptions);
     void setChildOptions(const QJsonArray &newChildOptions);
     void clearFormData();
     void setFormData(const HProject &Project);
-    int getOperationStatus() const;
-    void setOperationStatus(int newOperationStatus);
+    int getOperationType() const;
+    void setOperationType(int newOperationType);
 
    private slots:
     void on_comboBoxAddr_currentIndexChanged(int index);
     void on_comboBoxAddr_2_currentIndexChanged(int index);
-    void on_comboBoxOperator_currentIndexChanged(int index);
-    void on_comboBoxBlast_currentIndexChanged(int index);
-    void onBlasterItemChanged(QStandardItem *item);
-    void onOperatorItemChanged(QStandardItem *item);
 
    signals:
-    void validateDetNum(const QMap<QString, QString> &data);
-    void validateDetNumUpdate(const QMap<QString, QString> &data);
+    void createProject(const QMap<QString, QString> &data);
+    void saveProject(const QMap<QString, QString> &data);
 
    private:
-    void fillComboBox(QComboBox *comboBox, const QJsonArray &jsonArray);
     void updateL2AddressOptions();
     void updateL3AddressOptions();
     void validateAndSaveProject();
     void extractNames(const QJsonArray &array, QStringList &names);
-    void updateSelectedBlasters();
-    void updateSelectedOperators();
 
    private:
     Ui::ProjectDialog *ui;
-    QString blasterId;
-    QString operatorId;
+
+    QStringList selectedBlasters;
+    QStringList selectedOperators;
+    QStringList selectedSafetyInspectors;
     QJsonArray blasterArray;
     QJsonArray operatorArray;
+    QJsonArray safeGuardsArray;
     QJsonArray dataOptions;
     QJsonArray parentOptions;
     QJsonArray childOptions;
@@ -65,14 +61,7 @@ class ProjectDialog : public QDialog {
     QJsonArray L1AddressOptions;
     QJsonArray L2AddressOptions;
     QJsonArray L3AddressOptions;
-    QHash<QString, QString> nameLoraSnMap;
-    int operationStatus;
-
-    // 多选相关变量
-    QStringList selectedBlasters;
-    QStringList selectedOperators;
-    QStringList blasterIds;
-    QStringList operatorIds;
+    int operationType;
 };
 
 #endif  // PROJECTDIALOG_H

+ 43 - 33
blastProject/projectdialog.ui

@@ -6,15 +6,25 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>505</width>
-    <height>426</height>
+    <width>560</width>
+    <height>439</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Dialog</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
-   <item row="0" column="0">
+   <item row="2" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Orientation::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
     <layout class="QFormLayout" name="formLayout">
      <item row="0" column="0">
       <widget class="QLabel" name="labName">
@@ -53,9 +63,6 @@
        </property>
       </widget>
      </item>
-     <item row="3" column="1">
-      <widget class="QComboBox" name="comboBoxOperator"/>
-     </item>
      <item row="4" column="0">
       <widget class="QLabel" name="labBlastName">
        <property name="text">
@@ -63,17 +70,21 @@
        </property>
       </widget>
      </item>
-     <item row="4" column="1">
-      <widget class="QComboBox" name="comboBoxBlast"/>
-     </item>
      <item row="5" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>安全员:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="0">
       <widget class="QLabel" name="labAddress">
        <property name="text">
-        <string>井下地址</string>
+        <string>井下地址</string>
        </property>
       </widget>
      </item>
-     <item row="5" column="1">
+     <item row="6" column="1">
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
         <widget class="QComboBox" name="comboBoxAddr"/>
@@ -86,48 +97,47 @@
        </item>
       </layout>
      </item>
-     <item row="8" column="0">
-      <widget class="QLabel" name="labDetNum">
+     <item row="7" column="0">
+      <widget class="QLabel" name="label">
        <property name="text">
-        <string>雷管数量:</string>
+        <string>采场地址:</string>
        </property>
       </widget>
      </item>
-     <item row="8" column="1">
-      <widget class="QLineEdit" name="editDetNum"/>
+     <item row="7" column="1">
+      <widget class="QLineEdit" name="level4Address"/>
      </item>
-     <item row="7" column="0">
+     <item row="8" column="0">
       <widget class="QLabel" name="labRegCount">
        <property name="text">
         <string>起爆器数量:</string>
        </property>
       </widget>
      </item>
-     <item row="7" column="1">
+     <item row="8" column="1">
       <widget class="QLineEdit" name="editRegCount"/>
      </item>
-     <item row="6" column="0">
-      <widget class="QLabel" name="label">
+     <item row="9" column="0">
+      <widget class="QLabel" name="labDetNum">
        <property name="text">
-        <string>采场地址:</string>
+        <string>雷管数量:</string>
        </property>
       </widget>
      </item>
-     <item row="6" column="1">
-      <widget class="QLineEdit" name="level4Address"/>
+     <item row="9" column="1">
+      <widget class="QLineEdit" name="editDetNum"/>
+     </item>
+     <item row="4" column="1">
+      <widget class="QWidget" name="undergroundOperatorsWidget" native="true"/>
+     </item>
+     <item row="5" column="1">
+      <widget class="QWidget" name="safetyInspectorsWidget" native="true"/>
+     </item>
+     <item row="3" column="1">
+      <widget class="QWidget" name="groundOperatorsWidget" native="true"/>
      </item>
     </layout>
    </item>
-   <item row="1" column="0">
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="orientation">
-      <enum>Qt::Orientation::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
-     </property>
-    </widget>
-   </item>
   </layout>
  </widget>
  <resources/>

+ 148 - 0
components/MultiSelectComboBox.cpp

@@ -0,0 +1,148 @@
+#include "MultiSelectComboBox.h"
+
+#include <QDebug>
+#include <QEvent>
+#include <QLineEdit>
+#include <QStandardItem>
+#include <QStandardItemModel>
+#include <QStyleOptionComboBox>
+#include <QStylePainter>
+
+MultiSelectComboBox::MultiSelectComboBox(QWidget *parent) : QComboBox(parent), skipNextHide(false) { initialize(); }
+
+MultiSelectComboBox::~MultiSelectComboBox() {}
+
+void MultiSelectComboBox::initialize() {
+    // Set up the model
+    setModel(new QStandardItemModel(this));
+
+    // Set up the view
+    QListView *listView = new QListView(this);
+    setView(listView);
+
+    // Make the combo box read-only
+    setEditable(true);
+    setInsertPolicy(QComboBox::NoInsert);
+    lineEdit()->setReadOnly(true);
+
+    // Install event filter to handle mouse events
+    view()->viewport()->installEventFilter(this);
+
+    // Connect to the item changed signal
+    connect(model(), &QStandardItemModel::itemChanged, this, &MultiSelectComboBox::onItemChanged);
+
+    // Set minimum width for better display
+    setMinimumWidth(150);
+
+    // Set the default text
+    updateText();
+}
+
+void MultiSelectComboBox::addItem(const QString &key, const QString &value) {
+    QStandardItem *item = new QStandardItem(value);
+    item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
+    item->setData(Qt::Unchecked, Qt::CheckStateRole);
+    item->setData(key, Qt::UserRole);
+
+    model()->appendRow(item);
+    updateText();
+}
+
+void MultiSelectComboBox::addItems(const QList<QPair<QString, QString>> &items) {
+    for (const auto &item : items) {
+        addItem(item.first, item.second);
+    }
+}
+
+QStringList MultiSelectComboBox::checkedKeys() const {
+    QStringList keys;
+    for (int i = 0; i < model()->rowCount(); ++i) {
+        QStandardItem *item = model()->item(i);
+        if (item->checkState() == Qt::Checked) {
+            keys.append(item->data(Qt::UserRole).toString());
+        }
+    }
+    return keys;
+}
+
+QList<QPair<QString, QString>> MultiSelectComboBox::checkedItemsWithKeys() const {
+    QList<QPair<QString, QString>> items;
+    for (int i = 0; i < model()->rowCount(); ++i) {
+        QStandardItem *item = model()->item(i);
+        if (item->checkState() == Qt::Checked) {
+            items.append({item->data(Qt::UserRole).toString(), item->text()});
+        }
+    }
+    return items;
+}
+
+void MultiSelectComboBox::setCheckedKeys(const QStringList &keys) {
+    for (int i = 0; i < model()->rowCount(); ++i) {
+        QStandardItem *item = model()->item(i);
+        QString key = item->data(Qt::UserRole).toString();
+        item->setData(keys.contains(key) ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
+    }
+    updateText();
+}
+
+bool MultiSelectComboBox::eventFilter(QObject *object, QEvent *event) {
+    if (object == view()->viewport()) {
+        if (event->type() == QEvent::MouseButtonRelease) {
+            skipNextHide = true;
+        }
+    }
+    return false;
+}
+
+void MultiSelectComboBox::showPopup() { QComboBox::showPopup(); }
+
+void MultiSelectComboBox::hidePopup() {
+    if (skipNextHide) {
+        skipNextHide = false;
+        return;
+    }
+    QComboBox::hidePopup();
+}
+
+void MultiSelectComboBox::onItemChanged(QStandardItem *item) {
+    Q_UNUSED(item);
+    updateText();
+}
+
+void MultiSelectComboBox::updateText() {
+    QStringList texts;
+    for (int i = 0; i < model()->rowCount(); ++i) {
+        QStandardItem *item = model()->item(i);
+        if (item->checkState() == Qt::Checked) {
+            texts.append(item->text());
+        }
+    }
+
+    displayText = texts.isEmpty() ? "请选择..." : texts.join(", ");
+    setEditText(displayText);
+    emit selectionChanged();
+}
+
+void MultiSelectComboBox::paintEvent(QPaintEvent *event) {
+    Q_UNUSED(event);
+    QStylePainter painter(this);
+    painter.setPen(palette().color(QPalette::Text));
+
+    // Draw the combobox frame, focusrect and selected etc.
+    QStyleOptionComboBox opt;
+    initStyleOption(&opt);
+
+    // If we have selected items, use displayText, otherwise use placeholderText
+    if (!displayText.isEmpty()) {
+        opt.currentText = displayText;
+    } else {
+        opt.currentText = "Select items...";
+    }
+
+    painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+    painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+}
+
+QStandardItemModel *MultiSelectComboBox::model() const {
+    return qobject_cast<QStandardItemModel *>(QComboBox::model());
+}

+ 47 - 0
components/MultiSelectComboBox.h

@@ -0,0 +1,47 @@
+#ifndef MULTISELECTCOMBOBOX_H
+#define MULTISELECTCOMBOBOX_H
+
+#include <QAbstractItemView>
+#include <QComboBox>
+#include <QListView>
+#include <QPainter>
+#include <QPair>
+#include <QStandardItem>
+#include <QStandardItemModel>
+
+class MultiSelectComboBox : public QComboBox {
+    Q_OBJECT
+
+   public:
+    explicit MultiSelectComboBox(QWidget *parent = nullptr);
+    ~MultiSelectComboBox();
+
+    void addItem(const QString &key, const QString &value);
+    void addItems(const QList<QPair<QString, QString>> &items);
+
+    QStringList checkedKeys() const;
+    QList<QPair<QString, QString>> checkedItemsWithKeys() const;
+    void setCheckedKeys(const QStringList &keys);
+
+   protected:
+    bool eventFilter(QObject *object, QEvent *event) override;
+    void showPopup() override;
+    void hidePopup() override;
+    void paintEvent(QPaintEvent *event) override;
+
+   private slots:
+    void onItemChanged(QStandardItem *item);
+    void updateText();
+
+   private:
+    void initialize();
+    QStandardItemModel *model() const;
+
+    QString displayText;
+    bool skipNextHide;
+
+   signals:
+    void selectionChanged();
+};
+
+#endif  // MULTISELECTCOMBOBOX_H

+ 5 - 7
hprojectdao.cpp

@@ -6,11 +6,10 @@ bool HProjectDao::addHProject(const HProject &project) {
     QSqlQuery query;
     query.prepare(
         "INSERT INTO h_project (uuid, name, company_code, htid, xmbh, operator_name, "
-        "operator_identity, blaster_name, blaster_identity, address_uuid, pc_sn, det_sum, "
-        "file_name, file_url, blast_status, created_at, updated_at, deleted_at, create_by, "
-        "update_by) "
-        "VALUES (:uuid, :name, :companyCode, :htid, :xmbh, :operatorName, :operatorIdentity, "
-        ":blasterName, :blasterIdentity, :addressUuid, :pcSn, :detSum, :fileName, :fileUrl, "
+        "operator_identity, blaster_identity, address_uuid, pc_sn, det_sum, safetyInspectorIdentity, "
+        "file_name, file_url, blast_status, created_at, updated_at, deleted_at, create_by, update_by) "
+        "VALUES (:uuid, :name, :companyCode, :htid, :xmbh, :operatorIdentity, :safetyInspectorIdentity "
+        ":blasterIdentity, :addressUuid, :pcSn, :detSum, :fileName, :fileUrl, "
         ":blastStatus, :createdAt, :updatedAt, :deletedAt, :createBy, :updateBy)");
 
     query.bindValue(":uuid", project.getUuid());
@@ -18,10 +17,9 @@ bool HProjectDao::addHProject(const HProject &project) {
     query.bindValue(":companyCode", project.getCompanyCode());
     query.bindValue(":htid", project.getHtid());
     query.bindValue(":xmbh", project.getXmbh());
-    query.bindValue(":operatorName", project.getOperatorName());
     query.bindValue(":operatorIdentity", project.getOperatorIdentity());
-    query.bindValue(":blasterName", project.getBlasterName());
     query.bindValue(":blasterIdentity", project.getBlasterIdentity());
+    query.bindValue(":safetyInspectorIdentity", project.getBlasterIdentity());
     query.bindValue(":addressUuid", project.getAddressUuid());
     query.bindValue(":pcSn", project.getPcSn());
     query.bindValue(":detSum", project.getDetSum());