4 Commits fb5f80be84 ... ac2bbba5a5

Author SHA1 Message Date
  YaoH ac2bbba5a5 dev: enable multiple selection for operators 3 weeks ago
  YaoH 18823bfc2a 更新命名 3 weeks ago
  YaoH ca60647f08 爆破员改为多选 3 weeks ago
  YaoH 16091cb032 updates: 删除设备地址;添加采场地址 3 weeks ago

+ 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

+ 1 - 1
README.md

@@ -34,7 +34,7 @@
 4. 有些请求为什么会失败?
    目前一些请求需要cookie, 所以需要从登录页面进入才可以。
 5. 如何添加用户?
-   访问后端APP。菜单: 基础信息 -> 人员管理。添加爆破员,操作员。
+   访问后端APP。菜单: 基础信息 -> 人员管理。添加井下爆破员,井上爆破员。
 
 6. 安全员控制逻辑。
    安全员汇报的地址可能包含多个爆破工程,但一个爆破工程不会上报多次地址。

+ 58 - 27
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,8 +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();
@@ -64,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) {
@@ -95,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());
                 }
@@ -143,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)) {  // 操作
+                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) {
@@ -171,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()) {
@@ -195,23 +219,22 @@ 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.setLoraAddress(data.value("loraAddress"));
-    project.setLoraSn(data.value("loraSn"));
+    project.setSafetyInspectorIdentity(data.value("safetyInspectorIdentity"));
+    project.setLevel4Address(data.value("level4Address"));
     project.setBlastCount(data.value("blastCount"));
     project.setBlastStatus("1");
     project.setCompanyCode(COMPANY_CODE);
@@ -244,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) {};
@@ -255,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"));
@@ -294,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

+ 8 - 16
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; }
 
@@ -91,17 +89,13 @@ qint64 HProject::getUpdateBy() const { return updateBy; }
 
 void HProject::setUpdateBy(qint64 newUpdateBy) { updateBy = newUpdateBy; }
 
-QString HProject::getLoraAddress() const { return loraAddress; }
-
-void HProject::setLoraAddress(const QString &newLoraAddress) { loraAddress = newLoraAddress; }
-
 QString HProject::getBlastCount() const { return blastCount; }
 
 void HProject::setBlastCount(const QString &newBlastCount) { blastCount = newBlastCount; }
 
-QString HProject::getLoraSn() const { return loraSn; }
+QString HProject::getLevel4Address() const { return level4Address; }
 
-void HProject::setLoraSn(const QString &newLoraSn) { loraSn = newLoraSn; }
+void HProject::setLevel4Address(const QString &newLevel4Address) { level4Address = newLevel4Address; }
 
 QByteArray HProject::ProjectToJson(const HProject &project) {
     QJsonObject jsonObject;
@@ -111,13 +105,11 @@ 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["loraAddress"] = project.getLoraAddress();
-    jsonObject["loraSn"] = project.getLoraSn();
+    jsonObject["safetyInspectorIdentity"] = project.getSafetyInspectorIdentity();
+    jsonObject["level4Address"] = project.getLevel4Address();
     jsonObject["blastCount"] = project.getBlastCount();
     QJsonDocument jsonDoc(jsonObject);
     return jsonDoc.toJson(QJsonDocument::Compact);

+ 9 - 19
blastProject/hproject.h

@@ -22,14 +22,12 @@ 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)
-    Q_PROPERTY(QString loraAddress READ getLoraAddress WRITE setLoraAddress)
-    Q_PROPERTY(QString loraSn READ getLoraSn WRITE setLoraSn)
+    Q_PROPERTY(QString level4Address READ getLevel4Address WRITE setLevel4Address)
     Q_PROPERTY(QString pcSn READ getPcSn WRITE setPcSn)
     Q_PROPERTY(QString detSum READ getDetSum WRITE setDetSum)
     Q_PROPERTY(QString blastCount READ getBlastCount WRITE setBlastCount)
@@ -64,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);
 
@@ -114,14 +106,14 @@ class HProject : public QObject {
 
     QByteArray ProjectToJson(const HProject &project);
 
-    QString getLoraAddress() const;
-    void setLoraAddress(const QString &newLoraAddress);
-
     QString getBlastCount() const;
     void setBlastCount(const QString &newBlastCount);
 
-    QString getLoraSn() const;
-    void setLoraSn(const QString &newLoraSn);
+    QString getLevel4Address() const;
+    void setLevel4Address(const QString &newLoraSn);
+
+    QString getSafetyInspectorIdentity() const;
+    void setSafetyInspectorIdentity(const QString &newSafetyInspectorIdentity);
 
    private:
     qint64 id;
@@ -130,14 +122,12 @@ class HProject : public QObject {
     QString companyCode;
     QString htid;
     QString xmbh;
-    QString operatorName;
     QString operatorIdentity;
-    QString blasterName;
+    QString safetyInspectorIdentity;
     QString blasterIdentity;
     QString addressUuid;
     QString addressPath;
-    QString loraAddress;
-    QString loraSn;
+    QString level4Address;
     QString pcSn;
     QString detSum;
     QString blastCount;

+ 22 - 28
blastProject/hprojectdao.cpp

@@ -7,29 +7,25 @@ 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, "
-        "lora_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, :loraAddress, :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(":loraAddress", project.getLoraAddress());
+    query.bindValue(":level4Address", project.getLevel4Address());
     query.bindValue(":blastCount", project.getBlastCount());
     query.bindValue(":pcSn", project.getPcSn());
     query.bindValue(":detSum", project.getDetSum());
@@ -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());

+ 107 - 103
blastProject/projectdialog.cpp

@@ -1,7 +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) {
@@ -10,42 +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;
-    ui->comboBoxBlast->clear();
-    fillComboBox(ui->comboBoxBlast, bapoYuanArray);
-    ui->comboBoxBlast->setCurrentIndex(-1);
-}
-
-void ProjectDialog::SetComboBoxOperator(const QJsonArray &anQuanYuanArray) {
-    operatorArray = anQuanYuanArray;
-    ui->comboBoxOperator->clear();
-    fillComboBox(ui->comboBoxOperator, anQuanYuanArray);
-    ui->comboBoxOperator->setCurrentIndex(-1);
-}
+void ProjectDialog::initComboBoxBlaster(const QJsonArray &undergroundOperators) {
+    MultiSelectComboBox *comboBox = new MultiSelectComboBox(this);
 
-void ProjectDialog::on_comboBoxOperator_currentIndexChanged(int index) {
-    if (index >= 0 && index < operatorArray.size()) {
-        operatorId = operatorArray[index].toObject()["identity"].toString();
+    for (const QJsonValue &value : undergroundOperators) {
+        if (value.isObject()) {
+            QJsonObject obj = value.toObject();
+            if (obj.contains("nickName")) {
+                QString name = obj["nickName"].toString();
+                comboBox->addItem(obj["identity"].toString(), name);
+            }
+        }
     }
+    QVBoxLayout *layout = new QVBoxLayout(ui->undergroundOperatorsWidget);
+    layout->addWidget(comboBox);
 }
 
-void ProjectDialog::on_comboBoxBlast_currentIndexChanged(int index) {
-    if (index >= 0 && index < blasterArray.size()) {
-        blasterId = blasterArray[index].toObject()["identity"].toString();
+void ProjectDialog::initComboBoxSafetyInspectors(const QJsonArray &safeInspectors) {
+    MultiSelectComboBox *comboBox = new MultiSelectComboBox(this);
+
+    for (const QJsonValue &value : safeInspectors) {
+        if (value.isObject()) {
+            QJsonObject obj = value.toObject();
+            if (obj.contains("nickName")) {
+                QString name = obj["nickName"].toString();
+                comboBox->addItem(obj["identity"].toString(), name);
+            }
+        }
     }
+    QVBoxLayout *layout = new QVBoxLayout(ui->safetyInspectorsWidget);
+    layout->addWidget(comboBox);
 }
 
-void ProjectDialog::fillComboBox(QComboBox *comboBox, const QJsonArray &jsonArray) {
-    for (const QJsonValue &value : jsonArray) {
+void ProjectDialog::initComboBoxOperator(const QJsonArray &groundOperators) {
+    MultiSelectComboBox *comboBox = new MultiSelectComboBox(this);
+
+    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) {
@@ -76,37 +92,6 @@ void ProjectDialog::updateL3AddressOptions() {
 
 void ProjectDialog::setChildOptions(const QJsonArray &newDataOptions) { dataOptions = newDataOptions; }
 
-void ProjectDialog::SetComboBoxLora(const QString &grandChildName) {
-    ui->comboBoxLora->clear();
-    for (const QJsonValue &item : dataOptions) {
-        const QJsonArray &childrenArray = item["children"].toArray();
-        for (const QJsonValue &child : childrenArray) {
-            const QJsonObject &childObj = child.toObject();
-            const QJsonArray &grandChildrenArray = childObj["children"].toArray();
-            for (const QJsonValue &grandChild : grandChildrenArray) {
-                const QJsonObject &grandChildObj = grandChild.toObject();
-                if (grandChildObj["name"].toString() == grandChildName) {
-                    const QJsonArray &greatGrandChildrenArray = grandChildObj["children"].toArray();
-                    for (const QJsonValue &greatGrandChild : greatGrandChildrenArray) {
-                        const QJsonObject &greatGrandChildObj = greatGrandChild.toObject();
-                        qDebug() << "greatGrandChildObj " << greatGrandChildObj;
-                        QString name = greatGrandChildObj["name"].toString();
-                        QString loraSn = greatGrandChildObj["loraSn"].toString();
-                        ui->comboBoxLora->addItem(name);
-                        nameLoraSnMap.insert(name, loraSn);
-                        QHash<QString, QString>::const_iterator i;
-                        for (i = nameLoraSnMap.constBegin(); i != nameLoraSnMap.constEnd(); ++i) {
-                            qDebug() << "Key:" << i.key() << ", Value:" << i.value();
-                        }
-                    }
-                    break;
-                }
-            }
-        }
-    }
-    ui->comboBoxLora->setCurrentIndex(-1);
-}
-
 void ProjectDialog::extractNames(const QJsonArray &array, QStringList &names) {
     for (const auto &item : array) {
         if (item.isObject()) {
@@ -126,12 +111,20 @@ void ProjectDialog::clearFormData() {
     for (QLineEdit *lineEdit : lineEdits) {
         lineEdit->clear();
     }
-
-    QList<QComboBox *> comboBoxes = findChildren<QComboBox *>();
-    for (QComboBox *comboBox : comboBoxes) {
-        comboBox->setCurrentIndex(-1);
+    QList<MultiSelectComboBox *> multiSelectCombos = findChildren<MultiSelectComboBox *>();
+    for (MultiSelectComboBox *combo : multiSelectCombos) {
+        combo->setCheckedKeys(QStringList());
     }
-    // for if view the data, will hide the accept button
+    ui->comboBoxAddr->setCurrentIndex(-1);
+    ui->comboBoxAddr_2->setCurrentIndex(-1);
+    ui->comboBoxAddr_3->setCurrentIndex(-1);
+
+    // 清除选中数据
+    selectedBlasters.clear();
+    selectedOperators.clear();
+    selectedSafetyInspectors.clear();  // Add this line
+
+    // 显示确认按钮
     ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(true);
 }
 
@@ -150,26 +143,25 @@ void ProjectDialog::on_comboBoxAddr_2_currentIndexChanged(int index) {
     }
 }
 
-void ProjectDialog::on_comboBoxAddr_3_currentIndexChanged(int index) {
-    if (index >= 0 && index < L3AddressOptions.size()) {
-        QString grandChildName = L3AddressOptions[index].toObject()["uuid"].toString();
-        SetComboBoxLora(grandChildName);
+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;
     }
-}
 
-void ProjectDialog::validateAndSaveProject() {
     QString detNum = ui->editDetNum->text().trimmed();
     QString blastCount = ui->editRegCount->text().trimmed();
     QString projectName = ui->editName->text().trimmed();
-    QString blastName = ui->comboBoxBlast->currentText().trimmed();
-    QString operatorName = ui->comboBoxOperator->currentText().trimmed();
-    QString l1AddressUuid = ui->comboBoxAddr->currentData().toString().trimmed();
-    QString l2AddressUuid = ui->comboBoxAddr_2->currentData().toString().trimmed();
-    QString l3AddressUuid = ui->comboBoxAddr_3->currentData().toString().trimmed();
-    QString l1AddressName = ui->comboBoxAddr->currentText().trimmed();
-    QString l2AddressName = ui->comboBoxAddr_2->currentText().trimmed();
-    QString l3AddressName = ui->comboBoxAddr_3->currentText().trimmed();
-    QString loraAddress = ui->comboBoxLora->currentText().trimmed();
     QString xmbh = ui->editXMBH->text().trimmed();
     QString htid = ui->editHTID->text().trimmed();
 
@@ -177,45 +169,64 @@ 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;
     data["detNum"] = detNum;
     data["name"] = projectName;
     data["xmbh"] = xmbh;
     data["htid"] = htid;
-    data["operatorName"] = operatorName;
-    data["blasterName"] = blastName;
-    // 选择level最低的uui:w
-    QString addressUuid = l1AddressUuid;
+
+    // 选择level最低的uuid
+    QString addressUuid = ui->comboBoxAddr->currentData().toString().trimmed();
+    QString l2AddressUuid = ui->comboBoxAddr_2->currentData().toString().trimmed();
+    QString l3AddressUuid = ui->comboBoxAddr_3->currentData().toString().trimmed();
+    QString l1AddressName = ui->comboBoxAddr->currentText().trimmed();
+    QString l2AddressName = ui->comboBoxAddr_2->currentText().trimmed();
+    QString l3AddressName = ui->comboBoxAddr_3->currentText().trimmed();
+
     if (l3AddressUuid != "") {
         addressUuid = l3AddressUuid;
     } else if (l2AddressUuid != "") {
         addressUuid = l2AddressUuid;
-    } else if (l1AddressUuid != "") {
-        addressUuid = l1AddressUuid;
-    } else {
+    } else if (addressUuid == "") {
         QMessageBox::warning(nullptr, "输入错误", "请选择地址!");
         return;
     }
+
     data["addressPath"] = l1AddressName + "/" + l2AddressName + "/" + l3AddressName;
     data["addressUuid"] = addressUuid;
-    data["blasterIdentity"] = blasterId;
-    data["operatorIdentity"] = operatorId;
-    data["loraAddress"] = loraAddress;
-    data["loraSn"] = nameLoraSnMap.value(loraAddress);
+    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();  // 清除表单数据
-    // this->accept();
 }
 
-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 {
@@ -228,18 +239,11 @@ void ProjectDialog::setFormData(const HProject &Project) {
         ui->editHTID->setText(Project.getHtid());
         ui->editXMBH->setText(Project.getXmbh());
         ui->editRegCount->setText(Project.getBlastCount());
-        int indexBlast = ui->comboBoxBlast->findText(Project.getBlasterName());
-        if (indexBlast != -1) {
-            ui->comboBoxBlast->setCurrentIndex(indexBlast);
-        } else {
-            qDebug() << "indexBlast 未找到选项 " << indexBlast << "。";
-        }
-        int indexOper = ui->comboBoxOperator->findText(Project.getOperatorName());
-        if (indexOper != -1) {
-            ui->comboBoxOperator->setCurrentIndex(indexOper);
-        } else {
-            qDebug() << "operator 未找到选项 " << Project.getOperatorName() << "。";
-        }
+        ui->level4Address->setText(Project.getLevel4Address());
+
+        // TODO: 这里需要处理井下爆破员和井上爆破员及安全员
+
+        // 设置地址选择
         QStringList addressParts = Project.getAddressPath().split("/");
         int numAddresses = addressParts.size();
         QVector<QString> addressVariables;

+ 15 - 21
blastProject/projectdialog.h

@@ -1,12 +1,13 @@
 #ifndef PROJECTDIALOG_H
 #define PROJECTDIALOG_H
 
-#include <QComboBox>
 #include <QDialog>
 #include <QHash>
 #include <QJsonArray>
 #include <QJsonObject>
+#include <QStandardItem>
 
+#include "../components/MultiSelectComboBox.h"
 #include "../regex.h"
 #include "hproject.h"
 
@@ -20,54 +21,47 @@ 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 on_comboBoxAddr_3_currentIndexChanged(int index);
-
    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 SetComboBoxLora(const QString &grandChildName);
     void extractNames(const QJsonArray &array, QStringList &names);
 
    private:
     Ui::ProjectDialog *ui;
-    QString blasterId;
-    QString operatorId;
+
+    QStringList selectedBlasters;
+    QStringList selectedOperators;
+    QStringList selectedSafetyInspectors;
     QJsonArray blasterArray;
     QJsonArray operatorArray;
+    QJsonArray safeGuardsArray;
     QJsonArray dataOptions;
     QJsonArray parentOptions;
-    QStringList loraOptions;
     QJsonArray childOptions;
     QJsonArray grandChildOptions;
     QJsonArray L1AddressOptions;
     QJsonArray L2AddressOptions;
     QJsonArray L3AddressOptions;
-    QHash<QString, QString> nameLoraSnMap;
-    int operationStatus;
+    int operationType;
 };
 
 #endif  // PROJECTDIALOG_H

+ 48 - 38
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">
@@ -49,31 +59,32 @@
      <item row="3" column="0">
       <widget class="QLabel" name="labOperaName">
        <property name="text">
-        <string>操作员:</string>
+        <string>井上爆破员:</string>
        </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">
-        <string>爆破员:</string>
+        <string>井下爆破员:</string>
        </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="6" column="0">
-      <widget class="QLabel" name="label">
+     <item row="8" column="0">
+      <widget class="QLabel" name="labRegCount">
        <property name="text">
-        <string>定位器地址:</string>
+        <string>起爆器数量:</string>
        </property>
       </widget>
      </item>
-     <item row="6" column="1">
-      <widget class="QComboBox" name="comboBoxLora"/>
+     <item row="8" column="1">
+      <widget class="QLineEdit" name="editRegCount"/>
      </item>
-     <item row="7" column="0">
-      <widget class="QLabel" name="labRegCount">
+     <item row="9" column="0">
+      <widget class="QLabel" name="labDetNum">
        <property name="text">
-        <string>起爆器数量:</string>
+        <string>雷管数量:</string>
        </property>
       </widget>
      </item>
-     <item row="7" column="1">
-      <widget class="QLineEdit" name="editRegCount"/>
+     <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/>

+ 2 - 2
blastopepage.cpp

@@ -260,8 +260,8 @@ void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
     headers = {
         {"选择", ""},
         {"工程名称", "name"},
-        {"操作员", "operatorName"},
-        {"爆破员", "blasterName"},
+        {"井上爆破员", "operatorName"},
+        {"井下爆破员", "blasterName"},
         {"井下地址", "addressPath"},
         {"雷管数量", "detSum"},
         {"起爆器数量", "blastCount"},

+ 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());