Forráskód Böngészése

修复Bug: 1. 作业按钮不刷新内容 2. 只有一个任务时显示问题 3. 取消"取消"按钮

xj 3 hónapja
szülő
commit
467c66abff

+ 48 - 29
src/httpclient/HttpClient.cpp

@@ -228,25 +228,25 @@ void HttpClient::run()
             m_workStat = httpWorkStat::httpWorkSleep;
             m_mutex.unlock();
 
-            // GET 请求在工作线程完成,在此发射信号,避免在主线程阻塞
-            if (postUrl == Config()->jobTicketsUrl) {
-                emit signalResponseGetJobTickets(bb);
-            }
-            else if (postUrl == Config()->workNodeDetail) {
-                emit signalResponseGetWorkNodeDetail(bb);
-            }
-            else if (postUrl == Config()->workNodeDetailForm) {
-                emit signalResponseGetFormById(bb);
-            }
-            else if (postUrl == Config()->userInfoUrl) {
-                emit signalResponseGetUserInfoUrl(bb);
-            }
-            else if (postUrl == Config()->keyMACByNFC) {
-                emit signalResponseGetKeyMAC(bb);
-            }
-            else if (postUrl == Config()->isolationPointById) {
-                emit signalResponseGetIsolationPointInfo(bb);
-            }
+//            // GET 请求在工作线程完成,在此发射信号,避免在主线程阻塞
+//            if (postUrl == Config()->jobTicketsUrl) {
+//                emit signalResponseGetJobTickets(bb);
+//            }
+//            else if (postUrl == Config()->workNodeDetail) {
+//                emit signalResponseGetWorkNodeDetail(bb);
+//            }
+//            else if (postUrl == Config()->workNodeDetailForm) {
+//                emit signalResponseGetFormById(bb);
+//            }
+//            else if (postUrl == Config()->userInfoUrl) {
+//                emit signalResponseGetUserInfoUrl(bb);
+//            }
+//            else if (postUrl == Config()->keyMACByNFC) {
+//                emit signalResponseGetKeyMAC(bb);
+//            }
+//            else if (postUrl == Config()->isolationPointById) {
+//                emit signalResponseGetIsolationPointInfo(bb);
+//            }
         }
 
         msleep(10);
@@ -559,7 +559,7 @@ QString HttpClient::putJsonRequest()
             QByteArray result = reply->readAll();
             QString strRes = QString::fromUtf8(result);
 
-            // qDebug() << "http post success post response=" << strRes;
+            qDebug() << "http post success post response=" << strRes;
             reply->deleteLater();
             return strRes;
         }
@@ -592,7 +592,7 @@ void HttpClient::slotPostRequestData(quint64 id, QString postUrl, QByteArray dat
     qDebug() << "[HttpClient]   URL:" << postUrl;
     qDebug() << "[HttpClient]   数据:" << QString::fromUtf8(data);
     
-    m_mutex.lock();
+//    m_mutex.lock();
     this->m_id = id;
     this->m_postUrl = postUrl;
     this->m_httpData = data;
@@ -609,7 +609,7 @@ void HttpClient::slotPostRequestData(quint64 id, QString postUrl, QByteArray dat
         QString res = postJsonRequest();
         qDebug() << "[HttpClient] postJsonRequest完成,响应:" << res.left(200);
         QByteArray bb = res.toUtf8();
-        m_mutex.unlock();
+//        m_mutex.unlock();
 
         if(m_postUrl == Config()->usernameLogin_url) {
             emit signalResponsePasswordLoginData(bb);
@@ -648,21 +648,40 @@ void HttpClient::slotPostRequestData(quint64 id, QString postUrl, QByteArray dat
 
 void HttpClient::slotGetRequestData(quint64 id, QString postUrl, QByteArray data, QString token)
 {
-    // 不在主线程执行同步 getRequest(),否则会阻塞 UI 导致“无响应”
-    // 将参数交给工作线程,由 run() 中执行 getRequest() 并发射信号
-    m_mutex.lock();
+//    m_mutex.lock();
     this->m_id = id;
     this->m_postUrl = postUrl;
     this->m_httpData = data;
     this->m_token = token;
     HttpClient::sToken = token;
-    this->m_workStat = httpWorkStat::httpWorkGet;
-    m_mutex.unlock();
+
+    QString res = getRequest();
+    QByteArray bb = res.toUtf8();
+//    m_mutex.unlock();
+
+    if (m_postUrl == Config()->jobTicketsUrl) {
+        emit signalResponseGetJobTickets(bb);
+    }
+    else if (m_postUrl == Config()->workNodeDetail) {
+        emit signalResponseGetWorkNodeDetail(bb);
+    }
+    else if (m_postUrl == Config()->workNodeDetailForm) {
+        emit signalResponseGetFormById(bb);
+    }
+    else if (m_postUrl == Config()->userInfoUrl) {
+        emit signalResponseGetUserInfoUrl(bb);
+    }
+    else if (m_postUrl == Config()->keyMACByNFC) {
+        emit signalResponseGetKeyMAC(bb);
+    }
+    else if (m_postUrl == Config()->isolationPointById) {
+        emit signalResponseGetIsolationPointInfo(bb);
+    }
 }
 
 void HttpClient::slotPutRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token)
 {
-    m_mutex.lock();
+//    m_mutex.lock();
     this->m_id = id;
     this->m_postUrl = postUrl;
     this->m_httpData = data;
@@ -677,7 +696,7 @@ void HttpClient::slotPutRequestData(quint64 id, QString postUrl, QByteArray data
     {
         QString res = putJsonRequest();
         QByteArray bb = res.toUtf8();
-        m_mutex.unlock();
+//        m_mutex.unlock();
 
         if(m_postUrl == Config()->updateUserInfoUrl) {
             emit signalReponseUpdateUserInfoUrl(bb);

+ 21 - 0
src/httpclient/JobTicketsModel.cpp

@@ -186,3 +186,24 @@ int JobTicketModel::getFirstRunningIndex() const
     }
     return -1;
 }
+
+QVariantMap JobTicketModel::getSingleJobTicket() const
+{
+    QVariantMap jobTicket;
+
+    for (int i = 0; i < m_dataList.count(); i++) {
+        if (m_dataList[i].m_approvalStatus == "进行中") {
+            JobTicket ticket = m_dataList[i];
+            jobTicket.insert("nodeId", QString::number(ticket.m_nodeId));
+            jobTicket.insert("name", ticket.m_name);
+            jobTicket.insert("orderNo", ticket.m_orderNo);
+            jobTicket.insert("approvalStatus", ticket.m_approvalStatus);
+            jobTicket.insert("workTime", ticket.m_workTime);
+            jobTicket.insert("workerUserName", ticket.m_workerUserName);
+            jobTicket.insert("currentNodeName", ticket.m_currentNodeName);
+            return jobTicket;
+        }
+    }
+
+    return jobTicket;
+}

+ 1 - 0
src/httpclient/JobTicketsModel.h

@@ -85,6 +85,7 @@ public:
     Q_INVOKABLE int getFirstRunningNodeId() const;
     // 获取第一个"进行中"作业的索引,如果没有返回-1
     Q_INVOKABLE int getFirstRunningIndex() const;
+    Q_INVOKABLE QVariantMap getSingleJobTicket() const;
     virtual QHash<int, QByteArray> roleNames() const override;
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
 private:

+ 2 - 1
src/httpclient/WorkNodeFormModel.h

@@ -36,7 +36,7 @@ class WorkNodeFormModel : public QAbstractListModel
     QML_SINGLETON
     QML_NAMED_ELEMENT(WorkNodeFormModel)
 
-    Q_PROPERTY(QString modelType READ modelType WRITE setModelType)
+    Q_PROPERTY(QString modelType READ modelType WRITE setModelType NOTIFY modelTypeChanged)
     Q_PROPERTY(QString formJsonInfo READ formJsonInfo WRITE setFormJsonInfo NOTIFY formJsonInfoChanged)
     Q_PROPERTY(QJsonArray nodeUserList READ nodeUserList WRITE setNodeUserList NOTIFY nodeUserListChanged)
     Q_PROPERTY(int workId READ workId WRITE setWorkId NOTIFY workIdChanged)
@@ -142,6 +142,7 @@ public:
         emit descriptionChanged();
     }
 signals:
+    void modelTypeChanged();
     void formJsonInfoChanged();
     void nodeUserListChanged();
     void workIdChanged();

+ 50 - 16
src/interactive/InteractiveCAN.cpp

@@ -6,17 +6,17 @@
 #include <QDebug>
 
 // ============ 临时禁用CAN通讯日志 (可撤回) ============
-#define CAN_LOG_DISABLED
-#ifdef CAN_LOG_DISABLED
-#undef qDebug
-#undef qInfo
-#undef qWarning
-#undef qCritical
-#define qDebug() QNoDebug()
-#define qInfo() QNoDebug()
-#define qWarning() QNoDebug()
-#define qCritical() QNoDebug()
-#endif
+//#define CAN_LOG_DISABLED
+//#ifdef CAN_LOG_DISABLED
+//#undef qDebug
+//#undef qInfo
+//#undef qWarning
+//#undef qCritical
+//#define qDebug() QNoDebug()
+//#define qInfo() QNoDebug()
+//#define qWarning() QNoDebug()
+//#define qCritical() QNoDebug()
+//#endif
 // ====================================================
 
 InteractiveCAN* InteractiveCAN::pInstance = nullptr;
@@ -61,6 +61,7 @@ InteractiveCAN::InteractiveCAN()
     connect(m_canClient, &CANClient::keyBaseChargeControlWritten, this, &InteractiveCAN::slotUnlockEKey);
     connect(m_canClient, &CANClient::lockBuckleWritten, this, &InteractiveCAN::slotUnlockLocks);
     connect(m_canClient, &CANClient::signalReturnKey, this, &InteractiveCAN::slotGetReturnCANDevices);
+    connect(m_canClient, &CANClient::signalLockNfcDeviceDetected, this, &InteractiveCAN::slotDeviceDetected);
 
     m_bleClient = new BLEClient(this);
 //    m_bleClient->start();
@@ -103,6 +104,7 @@ InteractiveCAN::InteractiveCAN()
     });
     connect(m_bleClient, &BLEClient::signalSendJobTicketStatus, this, &InteractiveCAN::slotSendJobTicketStatus);
     connect(m_bleClient, &BLEClient::workTicketResultReceived, this, &InteractiveCAN::slotReceivedJobTicketResult);
+    connect(m_bleClient, &BLEClient::signalFailConnect, this, &InteractiveCAN::slotReConnectBLE);
 
     m_checkHasKeyTimer = new QTimer(this);
     connect(m_checkHasKeyTimer, &QTimer::timeout, this, &InteractiveCAN::checkEKeyStatus);
@@ -337,7 +339,7 @@ void InteractiveCAN::httpRequestPostUpdateBackLock(const QList<QString> &lockNfc
 
     QJsonDocument jsonDoc(locksInfo);
     QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
-    qDebug() << jsonDoc;
+    qDebug() << "[httpRequestPostUpdateBackLock] " << jsonDoc;
 
     emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
 }
@@ -401,13 +403,14 @@ void InteractiveCAN::slotHttpResponseGetKeyMAC(QByteArray data)
 {
     QJsonParseError error;
     QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+//    qDebug() << "[slotHttpResponseGetKeyMAC]: " << jsonDoc;
     if (error.error != QJsonParseError::NoError) {
         return;
     }
     if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
         return;
     }
-    qDebug() << jsonDoc;
+
     QJsonObject rootObj = jsonDoc.object();
     if(rootObj.contains("code")) {
         int codeValue = rootObj.value("code").toInt();
@@ -428,13 +431,17 @@ void InteractiveCAN::slotHttpResponseGetIsolationPointInfo(QByteArray data)
 {
     QJsonParseError error;
     QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+
     if (error.error != QJsonParseError::NoError) {
         return;
     }
     if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
         return;
     }
-    ;
+    else {
+        qDebug() << "[InteractiveCAN::slotHttpResponseGetIsolationPointInfo]: " << jsonDoc;
+    }
+
     QJsonObject rootObj = jsonDoc.object();
     if(rootObj.contains("code")) {
         int codeValue = rootObj.value("code").toInt();
@@ -635,6 +642,13 @@ void InteractiveCAN::slotGetReturnCANDevices()
     emit signalReturnDevicesInfo(info);
 }
 
+void InteractiveCAN::slotReConnectBLE()
+{
+    if (m_bleClient) {
+        m_bleClient->connectDevice(m_currentKeyMAC, "keyLock");
+    }
+}
+
 InteractiveCAN *InteractiveCAN::instance()
 {
     if (!pInstance) {
@@ -670,7 +684,7 @@ void InteractiveCAN::openEKey()
             break;
         }
     }
-
+    qDebug() << "[InteractiveCAN] key nfc: " << m_keyNFC;
     if (!m_keyNFC.isEmpty()) {
         QJsonObject jsonRoot;
         QDateTime currentDateTime = QDateTime::currentDateTime();
@@ -843,10 +857,29 @@ void InteractiveCAN::slotUnlockLocks(bool success, const QList<QString>& lockRfi
                 });
             }
         }
+//        qDebug() << "[slotUnlockLocks]: " << lockRfids << WorkNodeFormModel::instance()->modelType();
+//        if (WorkNodeFormModel::instance()->modelType() == QString("releaseIsolation")) {
+//            if (!m_getLockedRfidFlag) {
+//                m_getLockedRfidFlag = true;
+//                qDebug() << "[slotUnlockLocks]: " << lockRfids;
+//                httpRequestPostUpdateBackLock(lockRfids);
+
+//                emit signalUpdateBackLockStatus(lockRfids);
+//            }
+//        }
+    }
+}
+
+void InteractiveCAN::slotDeviceDetected(const QString &nfcId)
+{
+    if (!nfcId.isEmpty()) {
         if (WorkNodeFormModel::instance()->modelType() == QString("releaseIsolation")) {
             if (!m_getLockedRfidFlag) {
                 m_getLockedRfidFlag = true;
-                qDebug() << "[slotUnlockLocks]: " << lockRfids;
+
+                QList<QString> lockRfids;
+                lockRfids << nfcId;
+                qDebug() << "[slotDeviceDetected]: " << nfcId << lockRfids;
                 httpRequestPostUpdateBackLock(lockRfids);
 
                 emit signalUpdateBackLockStatus(lockRfids);
@@ -869,6 +902,7 @@ void InteractiveCAN::slotOkSearchedBLE(bool success)
 {
     if (success) {
         if (m_bleClient->isConnected()) {
+            qDebug() << "[InteractiveCAN::slotOkSearchedBLE]";
             createColockJobTicket();
         }
         else {

+ 2 - 0
src/interactive/InteractiveCAN.h

@@ -81,6 +81,7 @@ public:
 public slots:
     void slotUnlockEKey(const CANKeyBaseChargeStatus& status);
     void slotUnlockLocks(bool success, const QList<QString>& lockRfids);
+    void slotDeviceDetected(const QString& nfcId);
     void slotSearchBLE();
     void slotOkSearchedBLE(bool success);
 
@@ -115,6 +116,7 @@ public slots:
     void slotUpdateColockStatus(const QString& cardNfc);
 
     void slotGetReturnCANDevices();
+    void slotReConnectBLE();
 signals:
     void signalReturnDevicesInfo(const QByteArray& info);
     void signalOkSearchedBLE(bool success);

+ 0 - 1
src/qml/JobTicketPage.qml

@@ -6,7 +6,6 @@ import Loto 1.0
 
 Rectangle {
     id: control
-    objectName: "__JobTicketPage__"
 
     property string pageTitleIconCharacter: "\uf0ae"
     property string pageTitleText: "我的作业"

+ 115 - 14
src/qml/Login.qml

@@ -147,24 +147,111 @@ Rectangle {
 
     // 作业票请求状态标志
     property bool isRequestingJobTickets: false
+    HttpGetWorkNodeDetail {
+        id: httpGetWorkNodeDetail
+    }
 
-    // 作业票
-    HttpGetJobTickets {
-        id: httpGetJobTickets
-
-        Component.onCompleted: {
-            httpGetJobTickets.signalGetRequestData.connect(httpClientThread.slotGetRequestData);
-            httpClientThread.signalResponseGetJobTickets.connect(httpGetJobTickets.slotHttpResponseGetJobTickets);
-        }
+    Connections {
+        target: httpGetWorkNodeDetail
+        function onSignalWorkNodeDetailReturnStat(state, msg) {
+            httpGetWorkNodeDetail.signalGetRequestData.disconnect(httpClientThread.slotGetRequestData);
+            httpClientThread.signalResponseGetWorkNodeDetail.disconnect(httpGetWorkNodeDetail.slotHttpResponseGetWorkNodeDetail);
+            httpClientThread.signalResponseGetFormById.disconnect(httpGetWorkNodeDetail.slotHttpResponseGetFormById);
+
+            var isShowNegativeBtn = false;
+            var isShowOpsitiveBtn = false;
+            var textNegativeBtnStr = "";
+            var textOpsitiveBtnStr = "";
+            var showJobTicketProcess = false;
+            var showFormCard = false;
+            var showJobTicketColockProcess = false;
+            var jobTicketinfo = JobTicketModel.getSingleJobTicket();
+
+            if (state !== 0) {
+                if (WorkNodeFormModel.modelType === "isolation") {
+                    // 隔离/方案
+                    showJobTicketProcess = true;
+                } else if (WorkNodeFormModel.modelType === "releaseIsolation" ||
+                            WorkNodeFormModel.modelType === "returnLock") {
+                    // 还锁 || 解除隔离
+                    showJobTicketColockProcess = true;
+                } else {
+                    showAlertDialog("提示", msg, "\uf071", negativeAlertDialogCallback);
+                    return;
+                }
+            } else {
+                if (WorkNodeFormModel.modelType === "inputInfo") {
+                    // 录入信息
+                    isShowNegativeBtn = false;
+                    isShowOpsitiveBtn = true;
+//                    textNegativeBtnStr = "取消";
+                    textOpsitiveBtnStr = "确认"
+                    showFormCard= true;
+                } else if (WorkNodeFormModel.modelType === "review") {
+                    // 审核
+                    isShowNegativeBtn = true;
+                    isShowOpsitiveBtn = true;
+                    textNegativeBtnStr = "审核不通过";
+                    textOpsitiveBtnStr = "审核通过";
+                    showFormCard= true;
+                } else if (WorkNodeFormModel.modelType === "confirm") {
+                    // 通过
+                    isShowNegativeBtn = false;
+                    isShowOpsitiveBtn = true;
+//                    textNegativeBtnStr = "取消";
+                    textOpsitiveBtnStr = "提交";
+                    showFormCard= true;
+                } else if (WorkNodeFormModel.modelType === "isolation") {
+                    // 隔离/方案
+                    showJobTicketProcess = true;
+                } else if (WorkNodeFormModel.modelType === "releaseIsolation" ||
+                           WorkNodeFormModel.modelType === "returnLock") {
+                    // 还锁 || 解除隔离
+                    showJobTicketColockProcess = true;
+                } else if (WorkNodeFormModel.modelType === "complete") {
+                    // 完成/结束
+                    isShowNegativeBtn = false;
+                    isShowOpsitiveBtn = true;
+//                    textNegativeBtnStr = "取消";
+                    textOpsitiveBtnStr = "完成";
+                    showFormCard= true;
+                }
+            }
 
-        Component.onDestruction: {
-            httpGetJobTickets.signalGetRequestData.disconnect(httpClientThread.slotGetRequestData);
-            httpClientThread.signalResponseGetJobTickets.disconnect(httpGetJobTickets.slotHttpResponseGetJobTickets);
+            // 如果状态是"已完成",隐藏操作按钮(仅查看模式)
+            if (jobTicketinfo.approvalStatus === "已完成") {
+                isShowNegativeBtn = false;
+                isShowOpsitiveBtn = false;
+            }
+            control.cardLoginLoading = false;
+            appStackView.push("WorkingPage.qml", {
+                                    titleIconCharacter: "\uf023",
+                                    titleText: jobTicketinfo.name,
+                                    sopIconCharacter: '\uf292',
+                                    sopInfoText: jobTicketinfo.orderNo,
+                                    workingStateCharacter: "\uf02b",
+                                    workingStateText: jobTicketinfo.approvalStatus,
+                                    workingTimeCharacter: "\uf1da",
+                                    workingTimeText: jobTicketinfo.workTime,
+                                    userIconCharacter: "\uf406",
+                                    userName: jobTicketinfo.workerUserName,
+
+                                    showJobTicketColockProcess: showJobTicketColockProcess,
+                                    showJobTicketProcess: showJobTicketProcess,
+                                    showFormCard: showFormCard,
+                                    showNegativeBtn: isShowNegativeBtn,
+                                    showOpsitiveBtn: isShowOpsitiveBtn,
+                                    textNegativeBtn: textNegativeBtnStr,
+                                    textOpsitiveBtn: textOpsitiveBtnStr,
+
+                                    currentNodeId: jobTicketinfo.nodeId,
+                                    currentNodeName: jobTicketinfo.currentNodeName
+                              });
         }
     }
 
     Connections {
-        target: httpGetJobTickets
+        target: appHttpGetJobTickets
         function onSignalJobTicketsReturnStat(stat, msg) {
             // 只有在本页面发起请求时才处理
             if (!isRequestingJobTickets) {
@@ -177,9 +264,23 @@ Rectangle {
             if (stat !== 0 || JobTicketModel.rowCount() === 0) {
                 appStackView.push("components/NoJobTicketDialog.qml");
             } else {
-                if (JobTicketModel.rowCount() === 1) {
+                var runningCount = JobTicketModel.runningCount();
+                if (runningCount === 1) {
                     // 直接跳转到作业票页面
-                    appStackView.push("JobTicketPage.qml");
+//                    appStackView.push("JobTicketPage.qml");
+                    var jobTicketinfo = JobTicketModel.getSingleJobTicket();
+                    if (jobTicketinfo.length === 0) {
+                        appStackView.push("JobTicketPage.qml");
+                    } else {
+                        control.cardLoginLoading = true;
+                        cardLoginLoadingDialog.loadingText = "正在查询作业任务..."
+                        httpGetWorkNodeDetail.nodeId = jobTicketinfo.nodeId;
+                        // 防止Repeater复制槽函数
+                        httpGetWorkNodeDetail.signalGetRequestData.connect(httpClientThread.slotGetRequestData);
+                        httpClientThread.signalResponseGetWorkNodeDetail.connect(httpGetWorkNodeDetail.slotHttpResponseGetWorkNodeDetail);
+                        httpClientThread.signalResponseGetFormById.connect(httpGetWorkNodeDetail.slotHttpResponseGetFormById);
+                        httpGetWorkNodeDetail.start();
+                    }
                 } else {
                     appStackView.push("JobTicketPage.qml");
                 }

+ 37 - 30
src/qml/WorkingPage.qml

@@ -64,6 +64,8 @@ Rectangle {
             if (stat !== 0) {
                 showAlertDialog("提示", msg, "\uf071", negativeAlertDialogCallback);
             } else {
+                // 任务提交成功后,再次访问"我的任务"用于刷新作业页面
+                appHttpGetJobTickets.start();
                 // 提交成功统一提示
                 showSuccessConfirmDialog("提交成功,继续操作?");
             }
@@ -275,28 +277,28 @@ Rectangle {
             }
 
             // 作业内容(仅审核界面显示,字段为 description)
-            Column {
-                id: __jobDescriptionArea
-                width: parent.width - 92
-                visible: control.showFormCard && WorkNodeFormModel.modelType === "review"
-                spacing: 8
-                anchors.left: parent.left
-                anchors.top: parent.top
-
-                Text {
-                    text: "作业内容:"
-                    color: "#888888"
-                    font.pixelSize: 16
-                }
-                Text {
-                    width: parent.width - 20
-                    text: WorkNodeFormModel.description
-                    color: "white"
-                    font.pixelSize: 15
-                    wrapMode: Text.Wrap
-                    visible: WorkNodeFormModel.description !== ""
-                }
-            }
+//            Column {
+//                id: __jobDescriptionArea
+//                width: parent.width - 92
+//                visible: control.showFormCard && WorkNodeFormModel.modelType === "review"
+//                spacing: 8
+//                anchors.left: parent.left
+//                anchors.top: parent.top
+
+//                Text {
+//                    text: "作业内容:"
+//                    color: "#888888"
+//                    font.pixelSize: 16
+//                }
+//                Text {
+//                    width: parent.width - 20
+//                    text: WorkNodeFormModel.description
+//                    color: "white"
+//                    font.pixelSize: 15
+//                    wrapMode: Text.Wrap
+//                    visible: WorkNodeFormModel.description !== ""
+//                }
+//            }
 
             FormCard {
                 id: formCard
@@ -401,13 +403,13 @@ Rectangle {
 
                         onClicked: {
                             // 审核不通过 - 也需要表单校验
-                            var formInfo = formCard.collectFormInputInfo();
-                            if (validateFormInfo(formInfo)) {
-                                httpUpdateNodeApproval.nodeId = control.currentNodeId;
-                                httpUpdateNodeApproval.approvalStatus = "rejected";
-                                httpUpdateNodeApproval.formData = formInfo;
-                                httpUpdateNodeApproval.start();
-                            }
+//                            var formInfo = formCard.collectFormInputInfo();
+//                            if (validateFormInfo(formInfo)) {
+//                                httpUpdateNodeApproval.nodeId = control.currentNodeId;
+//                                httpUpdateNodeApproval.approvalStatus = "rejected";
+//                                httpUpdateNodeApproval.formData = formInfo;
+//                                httpUpdateNodeApproval.start();
+//                            }
                         }
                     }
                 }
@@ -520,7 +522,12 @@ Rectangle {
         componentObj.visible = false;
         appAlertDialog.sourceComponent = null;
         componentObj.destroy();
-        // 不调用 refreshDataAndGoToList(),留在当前页
+        // 返回"我的作业"页面
+        if (JobTicketModel.rowCount() === 0) {
+            stackView.replace("components/NoJobTicketDialog.qml");
+        } else {
+            stackView.replace("JobTicketPage.qml", { autoNavigateToDetail: false });
+        }
     }
     
     // 成功后退出系统 - 返回首页

+ 6 - 6
src/qml/components/JobTicketCard.qml

@@ -89,9 +89,9 @@ Rectangle {
             } else {
                 if (WorkNodeFormModel.modelType === "inputInfo") {
                     // 录入信息
-                    isShowNegativeBtn = true;
+                    isShowNegativeBtn = false;
                     isShowOpsitiveBtn = true;
-                    textNegativeBtnStr = "取消";
+//                    textNegativeBtnStr = "取消";
                     textOpsitiveBtnStr = "确认"
                     showFormCard= true;
                 } else if (WorkNodeFormModel.modelType === "review") {
@@ -103,9 +103,9 @@ Rectangle {
                     showFormCard= true;
                 } else if (WorkNodeFormModel.modelType === "confirm") {
                     // 通过
-                    isShowNegativeBtn = true;
+                    isShowNegativeBtn = false;
                     isShowOpsitiveBtn = true;
-                    textNegativeBtnStr = "取消";
+//                    textNegativeBtnStr = "取消";
                     textOpsitiveBtnStr = "提交";
                     showFormCard= true;
                 } else if (WorkNodeFormModel.modelType === "isolation") {
@@ -117,9 +117,9 @@ Rectangle {
                     showJobTicketColockProcess = true;
                 } else if (WorkNodeFormModel.modelType === "complete") {
                     // 完成/结束
-                    isShowNegativeBtn = true;
+                    isShowNegativeBtn = false;
                     isShowOpsitiveBtn = true;
-                    textNegativeBtnStr = "取消";
+//                    textNegativeBtnStr = "取消";
                     textOpsitiveBtnStr = "完成";
                     showFormCard= true;
                 }

+ 10 - 7
src/qml/components/JobTicketColockProcess.qml

@@ -447,12 +447,15 @@ Rectangle {
                         jobTicketStatusValue: "打开钥匙扣>>"
                     }
 
-//                    JobTicketSubProcess {
-//                        id: subProcess4
-//                        anchors.horizontalCenter: parent.horizontalCenter
-//                        width: 801
-//                        height: 99
-//                    }
+                    JobTicketSubProcess {
+                        id: subProcess4
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        width: 801
+                        height: 99
+
+                        jobTicketStatusTypeName: "放回钥匙"
+                        jobTicketStatusValue: "放回钥匙>>"
+                    }
 
                     Rectangle {
                         id: __successFirstStep
@@ -805,7 +808,7 @@ Rectangle {
                 subProcess3.showSuccessfulColor = true;
             }
             if (InteractiveCAN.okUnlockKey) {
-//                subProcess4.showSuccessfulColor = true;
+                subProcess4.showSuccessfulColor = true;
                 __sleepTimer.stop();
                 __successFirstStep.visible = true;
             }

+ 1 - 0
src/qml/components/JobTicketProcess.qml

@@ -771,6 +771,7 @@ Rectangle {
                 subProcess4.showSuccessfulColor = true;
                 __sleepTimer.stop();
                 __successFirstStep.visible = true;
+                tabBar.currentIndex = 1;
             }
         }
     }

+ 16 - 0
src/qml/main.qml

@@ -29,6 +29,7 @@ ApplicationWindow {
     property int appLogoutSeconds: defaultLogoutSeconds
     property bool appControlLogoutSecondsFlag: false
     property string appLogoutText: "退出登录"
+    property alias appHttpGetJobTickets: httpGetJobTickets
 
     property string logoLabelText: "博士能量隔离系统"
     property var advertisementImageModel: [
@@ -354,6 +355,21 @@ ApplicationWindow {
         }
     }
 
+    // 作业票
+    HttpGetJobTickets {
+        id: httpGetJobTickets
+
+        Component.onCompleted: {
+            httpGetJobTickets.signalGetRequestData.connect(httpClientThread.slotGetRequestData);
+            httpClientThread.signalResponseGetJobTickets.connect(httpGetJobTickets.slotHttpResponseGetJobTickets);
+        }
+
+        Component.onDestruction: {
+            httpGetJobTickets.signalGetRequestData.disconnect(httpClientThread.slotGetRequestData);
+            httpClientThread.signalResponseGetJobTickets.disconnect(httpGetJobTickets.slotHttpResponseGetJobTickets);
+        }
+    }
+
     Item {
         id: contentWrapper
         anchors.fill: parent

+ 5 - 2
src/usr/BluetoothClient.cpp

@@ -204,11 +204,10 @@ void BLEClient::connectDevice(const QString& macAddress, const QString& name)
     // 释放旧控制器
     if (m_leController) {
         m_leController->disconnectFromDevice();
-        delete m_leController;
         m_leController = nullptr;
     }
     if (m_targetService) {
-        delete m_targetService;
+//        delete m_targetService;
         m_targetService = nullptr;
     }
 
@@ -222,6 +221,7 @@ void BLEClient::connectDevice(const QString& macAddress, const QString& name)
     m_leController = QLowEnergyController::createCentral(addr, localDev.address(), this);
 
     if (!m_leController) {
+        qDebug() << "[BLEClient::connectDevice]: 创建蓝牙控制器失败";
         emit connectStateChanged(QLowEnergyController::UnconnectedState, "创建蓝牙控制器失败");
         return;
     }
@@ -248,6 +248,9 @@ void BLEClient::connectDevice(const QString& macAddress, const QString& name)
                                          .arg(m_leController->errorString());
                     qWarning() << "[BLE] 控制器错误:" << errMsg;
 //                    releaseBluetoothResource();
+                    disconnectDevice();
+                    msleep(3000);
+                    emit signalFailConnect();
                     emit connectStateChanged(QLowEnergyController::UnconnectedState, errMsg);
                 });
 

+ 1 - 0
src/usr/BluetoothClient.h

@@ -173,6 +173,7 @@ signals:
     void cmdSendResult(bool success, const QString& cmdName, const QString& msg);
 signals:
     void signalSendJobTicketStatus(bool success);
+    void signalFailConnect();
 private slots:
     // 蓝牙扫描结果
     void onDeviceDiscovered(const QBluetoothDeviceInfo& device);

+ 5 - 4
src/usr/CANClient.cpp

@@ -454,7 +454,6 @@ void CANClient::slotUpdateLockedStatus(quint8 nodeId, const CANLocksControl &sta
 
     QMutexLocker locker(&m_insertDataMutex);
     m_locksControl.insert(nodeId, status);
-
     if (m_locksControlStatus.find(nodeId) == m_locksControlStatus.end()) return;
 
     bool isWrite = false;
@@ -932,7 +931,9 @@ void CANClient::readLockStatusAsync()
                         !m_locksRFIDStatus[nodeId].lockRFIDMap[i].isEmpty() &&
                         m_locksRFIDStatus[nodeId].lockRFIDMap[i] != "00000000") {
                         qInfo() << "[归还检测]" << (i+1) << "号锁NFC卡号:" << m_locksRFIDStatus[nodeId].lockRFIDMap[i];
+
                         emit nfcDeviceDetected(m_locksRFIDStatus[nodeId].lockRFIDMap[i], "lock", i);
+                        emit signalLockNfcDeviceDetected(m_locksRFIDStatus[nodeId].lockRFIDMap[i]);
                     } else {
                         qWarning() << "[归还检测]" << (i+1) << "号锁存在但无法读取NFC";
                         emit nfcDeviceError("lock", i);
@@ -1095,9 +1096,9 @@ void CANClient::writeLockBuckleAsync(const CANLocksControl &status)
                     lockRfids.append(lockRfidMap[lockNum]);
                 }
             }
-//            for (int i = 0 ; i < status.lockNums.length(); i++) {
-//                qInfo() << "[Lock] " << status.lockNums[i] << "号锁扣:" << (status.lockLockedMap[status.lockNums[i]] ? "锁住" : "解锁");
-//            }
+            for (int i = 0 ; i < status.lockNums.length(); i++) {
+                qInfo() << "[Lock] " << status.lockNums[i] << "号锁扣:" << (status.lockLockedMap[status.lockNums[i]] ? "锁住" : "解锁");
+            }
         }
         else {
             for (int i = 0 ; i < status.lockNums.length(); i++) {

+ 1 - 0
src/usr/CANClient.h

@@ -211,6 +211,7 @@ signals:
     // ========== 归还钥匙和锁检测信号 ==========
     // 当检测到NFC设备插入时发出(用于归还流程)
     void nfcDeviceDetected(const QString& nfcId, const QString& deviceType, int slotIndex);
+    void signalLockNfcDeviceDetected(const QString& nfcId);
     // 当检测到设备异常时发出(无法读取NFC)
     void nfcDeviceError(const QString& deviceType, int slotIndex);
     // ==========================================