Переглянути джерело

1. 新增设置页面功能
2. 修复部分页面Bug

xj 4 місяців тому
батько
коміт
843850924d
35 змінених файлів з 1566 додано та 384 видалено
  1. 9 0
      src/CMakeLists.txt
  2. 152 8
      src/httpclient/HttpClient.cpp
  3. 9 0
      src/httpclient/HttpClient.h
  4. 2 100
      src/httpclient/HttpGetJobTickets.cpp
  5. 0 1
      src/httpclient/HttpGetJobTickets.h
  6. 131 0
      src/httpclient/HttpGetUserInfo.cpp
  7. 26 0
      src/httpclient/HttpGetUserInfo.h
  8. 72 0
      src/httpclient/HttpUpdateUserInfo.cpp
  9. 69 0
      src/httpclient/HttpUpdateUserInfo.h
  10. 71 0
      src/httpclient/HttpUpdateUserPassword.cpp
  11. 43 0
      src/httpclient/HttpUpdateUserPassword.h
  12. 2 2
      src/httpclient/JobTicketsModel.h
  13. 27 0
      src/httpclient/UserInfoModel.cpp
  14. 147 0
      src/httpclient/UserInfoModel.h
  15. 5 0
      src/httpclient/WorkNodeFormModel.cpp
  16. 2 0
      src/httpclient/WorkNodeFormModel.h
  17. 53 5
      src/main.qml
  18. 2 2
      src/qml/JobTicketPage.qml
  19. 139 120
      src/qml/SettingPage.qml
  20. 12 12
      src/qml/WorkingPage.qml
  21. 50 10
      src/qml/components/AvatarCard.qml
  22. 38 41
      src/qml/components/FormCard.qml
  23. 26 26
      src/qml/components/JobTicketCard.qml
  24. 99 11
      src/qml/components/JobTicketProcess.qml
  25. 2 0
      src/qml/components/JobTicketSubProcess.qml
  26. 24 12
      src/qml/components/LockStatusCard.qml
  27. 2 1
      src/qml/components/MDatePicker.qml
  28. 8 7
      src/qml/components/MInput.qml
  29. 1 1
      src/qml/components/MRadioButton.qml
  30. 1 1
      src/qml/components/MSwitchButton.qml
  31. 2 1
      src/qml/components/MTextArea.qml
  32. 10 7
      src/qml/components/SettingFormCard.qml
  33. 322 0
      src/qml/components/UpdatePasswordDialog.qml
  34. 4 16
      src/qml/components/UserInfoCard.qml
  35. 4 0
      src/usr/config.h

+ 9 - 0
src/CMakeLists.txt

@@ -92,6 +92,15 @@ qt_add_qml_module(${PROJECT_NAME}
         usr/utils.h usr/utils.cpp
         usr/BluetoothClient.h usr/BluetoothClient.cpp
         QML_FILES qml/components/LockStatusCard.qml
+        SOURCES httpclient/HttpGetUserInfo.h
+        SOURCES httpclient/HttpGetUserInfo.cpp
+        SOURCES httpclient/UserInfoModel.h
+        SOURCES httpclient/UserInfoModel.cpp
+        SOURCES httpclient/HttpUpdateUserInfo.h
+        SOURCES httpclient/HttpUpdateUserInfo.cpp
+        SOURCES httpclient/HttpUpdateUserPassword.h
+        SOURCES httpclient/HttpUpdateUserPassword.cpp
+        QML_FILES qml/components/UpdatePasswordDialog.qml
 )
 
 qt_add_resources(${PROJECT_NAME} "resources"

+ 152 - 8
src/httpclient/HttpClient.cpp

@@ -11,7 +11,6 @@
 #include <QBuffer>
 #include <QJsonDocument>
 #include <QJsonObject>
-#include <QFile>
 
 QString HttpClient::sToken = QString();
 
@@ -136,6 +135,63 @@ bool HttpClient::postRequest(const QString &url, const QString &token, const QBy
     return false;
 }
 
+bool HttpClient::putRequest(const QString &url, const QString &token, const QByteArray &inData, QByteArray &outData)
+{
+    // 获取当前日期和时间
+    QDateTime currentDateTime = QDateTime::currentDateTime();
+    QString timetamp_str = currentDateTime.toString("yyyy-MM-ddTHH:mm:ssZ");
+
+    QNetworkAccessManager* pHttpMgr = new QNetworkAccessManager();
+    QString strUrl = url;
+    // 设置头信息
+    QNetworkRequest requestInfo;
+    requestInfo.setUrl(QUrl(strUrl));
+    requestInfo.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
+    requestInfo.setRawHeader("Module", "Android_Material");
+    requestInfo.setRawHeader("Authorization", token.toUtf8());
+    requestInfo.setRawHeader("tenant_id", Config()->tenant_id.toUtf8());
+
+    // 发送 post 请求
+    QNetworkReply* reply = pHttpMgr->put(requestInfo, inData);
+
+    // 添加超时处理,1ms 超时
+    QEventLoop eventLoop;
+    connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
+
+    // 比如设置 1ms 内完成请求,否则就认为是超时
+    QTimer::singleShot(HTTP_REQUEST_TIMEOUT, &eventLoop, &QEventLoop::quit);
+    eventLoop.exec();
+
+    if(reply->isFinished()){
+        if (reply->error() == QNetworkReply::NoError) {
+            // 正常结束,读取响应数据
+            QByteArray result = reply->readAll();
+            outData = result;
+            reply->deleteLater();
+            return true;
+        }
+        else {
+            // 异常结束
+            // 请求失败
+            QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+
+            outData = QString("{\"code\":" + statusCode.toString() + ",\"result\":\"" + reply->errorString() + "\"}").toUtf8();
+            reply->deleteLater();
+            return false;
+        }
+    }
+    else {
+        // 请求超时
+        disconnect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
+        reply->abort();
+    }
+
+    reply->deleteLater();
+
+    outData = "{\"code\":400,\"result\":\"failure\"}";
+    return false;
+}
+
 void HttpClient::run()
 {
     while(this->m_threadstatus)
@@ -383,14 +439,69 @@ QString HttpClient::getRequest()
             QString strRes = QString::fromUtf8(result);
             QStringList urlSplit = m_postUrl.split("/");
 
-            QFile file("C:/Users/HP/Documents/temp/" + urlSplit[urlSplit.length()-1] + + "_" + getJsonStr + ".json");
-            if (file.open(QIODevice::WriteOnly)) {
-                file.write(result);
-            }
-            else {
-                qDebug() << "C:/Users/HP/Documents/temp/" + urlSplit[urlSplit.length()-1] + + "_" + getJsonStr + ".json";
-            }
+            reply->deleteLater();
+            return strRes;
+        }
+        else {
+            // 异常结束
+            // 请求失败
+            QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
 
+            QString strRes = "{\"code\":" + statusCode.toString() + ",\"result\":\"" + reply->errorString() + "\"}";
+            reply->deleteLater();
+            return strRes;
+        }
+    }
+    else {
+        // 请求超时
+        disconnect(reply, &QNetworkReply::finished, &eventloop, &QEventLoop::quit);
+        reply->abort();
+    }
+
+    reply->deleteLater();
+
+    return "{\"code\":400,\"result\":\"failure\"}";
+}
+
+QString HttpClient::putJsonRequest()
+{
+    // 获取当前日期和时间
+    QDateTime currentDateTime = QDateTime::currentDateTime();
+    // qint64 timestampSeconds = currentDateTime.toSecsSinceEpoch();
+    QString timetamp_str = currentDateTime.toString("yyyy-MM-ddTHH:mm:ssZ");
+
+    QNetworkAccessManager* pHttpMgr = new QNetworkAccessManager();
+    QString url = "http://";
+    url = url + Config()->httpHost + m_postUrl;
+    // qDebug() << url;
+    // 设置头信息
+    QNetworkRequest requestInfo;
+    requestInfo.setUrl(QUrl(url));
+    requestInfo.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
+    requestInfo.setRawHeader("Module", "Android_Material");
+    QString auth = m_token;
+    requestInfo.setRawHeader("Authorization", auth.toUtf8());
+    requestInfo.setRawHeader("tenant-id", Config()->tenant_id.toUtf8());
+
+    // 发送 put 请求
+    QNetworkReply* reply = pHttpMgr->put(requestInfo, m_httpData);
+
+    // 添加超时处理,1ms 超时
+    QEventLoop eventloop;
+    connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
+
+    // 比如设置 1ms 内完成请求,否则就认为是超时
+    QTimer::singleShot(HTTP_REQUEST_TIMEOUT, &eventloop, &QEventLoop::quit);
+    eventloop.exec();
+
+    QByteArray array;
+    if(reply->isFinished()){
+        if (reply->error() == QNetworkReply::NoError) {
+            // 正常结束,读取响应数据
+            QByteArray result = reply->readAll();
+            QString strRes = QString::fromUtf8(result);
+
+            // qDebug() << "http post success post response=" << strRes;
             reply->deleteLater();
             return strRes;
         }
@@ -399,6 +510,7 @@ QString HttpClient::getRequest()
             // 请求失败
             QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
 
+            // qDebug() << "http post failed, error code = " << statusCode.toString() << " error info: " << reply->errorString();
             QString strRes = "{\"code\":" + statusCode.toString() + ",\"result\":\"" + reply->errorString() + "\"}";
             reply->deleteLater();
             return strRes;
@@ -408,6 +520,7 @@ QString HttpClient::getRequest()
         // 请求超时
         disconnect(reply, &QNetworkReply::finished, &eventloop, &QEventLoop::quit);
         reply->abort();
+        // qDebug() << "http post timeout";
     }
 
     reply->deleteLater();
@@ -472,6 +585,37 @@ void HttpClient::slotGetRequestData(quint64 id, QString postUrl, QByteArray data
     else if (m_postUrl == Config()->workNodeDetailForm) {
         emit signalResponseGetFormById(bb);
     }
+    else if (m_postUrl == Config()->userInfoUrl) {
+        emit signalResponseGetUserInfoUrl(bb);
+    }
+}
+
+void HttpClient::slotPutRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token)
+{
+    m_mutex.lock();
+    this->m_id = id;
+    this->m_postUrl = postUrl;
+    this->m_httpData = data;
+    this->m_httpFile = file;
+    this->m_token = token;
+    HttpClient::sToken = token;
+    if(!this->m_httpData.isNull() && !this->m_httpFile.isNull())
+    {
+        m_workStat = httpWorkStat::httpWorkFormdata;
+    }
+    else if(!this->m_httpData.isNull())
+    {
+        QString res = putJsonRequest();
+        QByteArray bb = res.toUtf8();
+        m_mutex.unlock();
+
+        if(m_postUrl == Config()->updateUserInfoUrl) {
+            emit signalReponseUpdateUserInfoUrl(bb);
+        }
+        else if (m_postUrl == Config()->updatePasswordUrl) {
+            emit signalReponseUpdateUserPasswordUrl(bb);
+        }
+    }
 }
 
 void HttpClient::slotSetThreadStop()

+ 9 - 0
src/httpclient/HttpClient.h

@@ -26,15 +26,19 @@ public:
 
     static bool getRequest(const QString &url, const QString &token, const QByteArray &inData, QByteArray &outData);
     static bool postRequest(const QString &url, const QString &token, const QByteArray &inData, QByteArray &outData);
+    static bool putRequest(const QString &url, const QString &token, const QByteArray &inData, QByteArray &outData);
 protected:
     void run() override;
 private:
     QString postJsonRequest(void);
     QString postFormdataRequest(void);
     QString getRequest(void);
+
+    QString putJsonRequest(void);
 public slots:
     void slotPostRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token);
     void slotGetRequestData(quint64 id, QString postUrl, QByteArray data, QString token);
+    void slotPutRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token);
     void slotSetThreadStop(void);
 signals:
     void signalResponsePasswordLoginData(QByteArray res);
@@ -46,6 +50,11 @@ signals:
     void signalResponseGetFormById(QByteArray res);
 
     void signalResponseUpdateNodeApprovalUrl(QByteArray res);
+
+    void signalResponseGetUserInfoUrl(QByteArray res);
+
+    void signalReponseUpdateUserInfoUrl(QByteArray res);
+    void signalReponseUpdateUserPasswordUrl(QByteArray res);
 public:
     static QString sToken;
 private:

+ 2 - 100
src/httpclient/HttpGetJobTickets.cpp

@@ -17,106 +17,8 @@ HttpGetJobTickets::~HttpGetJobTickets()
     cleanJobTicketsModel();
 }
 
-void HttpGetJobTickets::testTask()
-{
-    QFile file("C:/Users/HP/Documents/temp/MyJobTickets_bak.json");
-    if (!file.open(QIODevice::ReadOnly)) {
-        return;
-    }
-    QByteArray byteArr = file.readAll();
-    QJsonDocument doc = QJsonDocument::fromJson(byteArr);
-    QJsonObject rootObj = doc.object();
-    if(rootObj.contains("code"))
-    {
-        int codeValue = rootObj.value("code").toInt();
-        if(codeValue == 200 || codeValue == 0) {
-            if(rootObj.contains("data")) {
-                QJsonObject vDataObj = rootObj.value("data").toObject();
-                if (vDataObj.empty()) {
-                    cleanJobTicketsModel();
-                    return;
-                }
-                if (!vDataObj.contains("list")) {
-                    cleanJobTicketsModel();
-                    return;
-                }
-                if (!vDataObj.value("list").isArray()) {
-                    return;
-                }
-                cleanJobTicketsModel();
-
-                QJsonArray listData = vDataObj.value("list").toArray();
-                for (auto iter = listData.begin(); iter != listData.end(); ++iter) {
-                    if (!iter->isObject()) {
-                        continue;
-                    }
-                    QJsonObject eleObj = iter->toObject();
-                    // 解析单个作业票信息
-                    JobTicket ticket;
-                    if (eleObj.contains("workId")) {
-                        ticket.m_workId = eleObj.value("workId").toInt() ;
-                    }
-                    if (eleObj.contains("nodeId")) {
-                        ticket.m_nodeId = eleObj.value("nodeId").toInt() ;
-                    }
-                    if (eleObj.contains("orderNo") && eleObj.value("orderNo").isString()) {
-                        ticket.m_orderNo = eleObj.value("orderNo").toString() ;
-                    }
-                    if (eleObj.contains("name") && eleObj.value("name").isString()) {
-                        ticket.m_name = eleObj.value("name").toString() ;
-                    }
-                    if (eleObj.contains("urgencyLevel") && eleObj.value("urgencyLevel").isString()) {
-                        ticket.m_urgencyLevel = eleObj.value("urgencyLevel").toString() ;
-                    }
-                    if (eleObj.contains("completionTime") && eleObj.value("completionTime").isString()) {
-                        ticket.m_completionTime = eleObj.value("completionTime").toString() ;
-                    }
-                    if (eleObj.contains("cancellationReason") && eleObj.value("cancellationReason").isString()) {
-                        ticket.m_cancellationReason = eleObj.value("cancellationReason").toString() ;
-                    }
-                    if (eleObj.contains("initiatorName") && eleObj.value("initiatorName").isString()) {
-                        ticket.m_initiatorName = eleObj.value("initiatorName").toString() ;
-                    }
-                    if (eleObj.contains("workerUserName") && eleObj.value("workerUserName").isString()) {
-                        ticket.m_workerUserName = eleObj.value("workerUserName").toString() ;
-                    }
-                    if (eleObj.contains("workTime") && eleObj.value("workTime").isString()) {
-                        ticket.m_workTime = eleObj.value("workTime").toString() ;
-                    }
-                    if (eleObj.contains("currentNodeId") && eleObj.value("currentNodeId").isString()) {
-                        ticket.m_currentNodeId = eleObj.value("currentNodeId").toString() ;
-                    }
-                    if (eleObj.contains("currentNodeName") && eleObj.value("currentNodeName").isString()) {
-                        ticket.m_currentNodeName = eleObj.value("currentNodeName").toString() ;
-                    }
-                    if (eleObj.contains("approvalStatus") && eleObj.value("approvalStatus").isString()) {
-                        QString approvalStatus = eleObj.value("approvalStatus").toString() ;
-                        if (approvalStatus == "approved") {
-                            ticket.m_approvalStatus = "通过";
-                        } else if (approvalStatus == "unaudited") {
-                            ticket.m_approvalStatus = "未审核";
-                        } else if (approvalStatus == "pending") {
-                            ticket.m_approvalStatus = "未开始";
-                        } else {
-                            ticket.m_approvalStatus = approvalStatus;
-                        }
-                    }
-                    JobTicketModel::instance()->append(ticket);
-                    // 测试
-                    // if (JobTicketModel::instance()->rowCount() == 1) {
-                    //     emit signalJobTicketsReturnStat(0, "已查询到作业任务");
-                    //     return;
-                    // }
-                }
-                emit signalJobTicketsReturnStat(0, "已查询到作业任务");
-            }
-        }
-    }
-}
-
 void HttpGetJobTickets::run()
 {
-    // testTask();
     httpRequestGetJobTickets();
 }
 
@@ -211,7 +113,7 @@ void HttpGetJobTickets::slotHttpResponseGetJobTickets(QByteArray data)
                         qint64 workTimeStamp = eleObj.value("workTime").toDouble();
                         if (workTimeStamp > 0) {
                             QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(workTimeStamp);
-                            ticket.m_workTime = dateTime.toString("yyyy-MM-dd");
+                            ticket.m_workTime = dateTime.toString("yyyy-MM-dd HH:mm:ss");
                         }
                     }
                     if (eleObj.contains("currentNodeId") && eleObj.value("currentNodeId").isString()) {
@@ -223,7 +125,7 @@ void HttpGetJobTickets::slotHttpResponseGetJobTickets(QByteArray data)
                     if (eleObj.contains("approvalStatus") && eleObj.value("approvalStatus").isString()) {
                         QString status = eleObj.value("approvalStatus").toString() ;
                         if (status == "approved") {
-                            ticket.m_approvalStatus = "通过";
+                            ticket.m_approvalStatus = "已完成";
                             ticket.m_approvalStatusColor = "#0acb57";
                         }
                         else if (status == "running") {

+ 0 - 1
src/httpclient/HttpGetJobTickets.h

@@ -14,7 +14,6 @@ public:
     ~HttpGetJobTickets();
 protected:
     void run() override;
-    void testTask();
     void cleanJobTicketsModel();
 private:
     void httpRequestGetJobTickets(void);

+ 131 - 0
src/httpclient/HttpGetUserInfo.cpp

@@ -0,0 +1,131 @@
+#include "HttpGetUserInfo.h"
+
+#include <QJsonObject>
+
+#include "config.h"
+#include "InteractiveData.h"
+#include "UserInfoModel.h"
+
+HttpGetUserInfo::HttpGetUserInfo(QObject *parent)
+    : QThread(parent)
+{
+
+}
+
+HttpGetUserInfo::~HttpGetUserInfo()
+{
+
+}
+
+void HttpGetUserInfo::run()
+{
+    httpRequestGetUserInfo();
+}
+
+void HttpGetUserInfo::cleanUserInfoModel()
+{
+
+}
+
+void HttpGetUserInfo::httpRequestGetUserInfo()
+{
+    QJsonObject jsonRoot;
+    QDateTime currentDateTime = QDateTime::currentDateTime();
+    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();
+
+    QString url = Config()->userInfoUrl;
+
+    QJsonDocument jsonDoc(jsonRoot);
+    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
+
+    emit signalGetRequestData(timestampSeconds, url, jsonData, GetInteractiveData()->m_token);
+}
+
+void HttpGetUserInfo::slotHttpResponseGetUserInfo(QByteArray data)
+{
+    QJsonParseError error;
+    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+    if (error.error != QJsonParseError::NoError) {
+        emit signalUserInfoReturnStat(-3, "无法查询到用户信息");
+        return;
+    }
+    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
+        emit signalUserInfoReturnStat(-3, "无法查询到用户信息");
+        return;
+    }
+
+    QJsonObject rootObj = jsonDoc.object();
+    if(rootObj.contains("code"))
+    {
+        int codeValue = rootObj.value("code").toInt();
+        if(codeValue == 200 || codeValue == 0) {
+            if(rootObj.contains("data")) {
+                QJsonObject vDataObj = rootObj.value("data").toObject();
+                if (vDataObj.empty()) {
+                    return;
+                }
+
+                if (vDataObj.contains("id")) {
+                    UserInfoModel::instance()->setId(vDataObj.value("id").toInt());
+                }
+                if (vDataObj.contains("username")) {
+                    UserInfoModel::instance()->setUsername(vDataObj.value("username").toString());
+                }
+                if (vDataObj.contains("nickname")) {
+                    UserInfoModel::instance()->setNickname(vDataObj.value("nickname").toString());
+                }
+                if (vDataObj.contains("email")) {
+                    UserInfoModel::instance()->setEmail(vDataObj.value("email").toString());
+                }
+                if (vDataObj.contains("mobile")) {
+                    UserInfoModel::instance()->setMobile(vDataObj.value("mobile").toString());
+                }
+                if (vDataObj.contains("sex")) {
+                    UserInfoModel::instance()->setSex(vDataObj.value("sex").toInt());
+                }
+                if (vDataObj.contains("avatar")) {
+                    UserInfoModel::instance()->setAvatar(vDataObj.value("avatar").toString());
+                }
+                if (vDataObj.contains("loginIp")) {
+                    UserInfoModel::instance()->setLoginIp(vDataObj.value("loginIp").toString());
+                }
+                if (vDataObj.contains("loginDate")) {
+                    qint64 loginDate = vDataObj.value("loginDate").toDouble();
+                    if (loginDate > 0) {
+                        QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(loginDate);
+                        UserInfoModel::instance()->setLoginDate(dateTime.toString("yyyy-MM-dd HH:mm:ss"));
+                    }
+                }
+                if (vDataObj.contains("createTime")) {
+                    qint64 createTime = vDataObj.value("createTime").toDouble();
+                    if (createTime > 0) {
+                        QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(createTime);
+                        UserInfoModel::instance()->setCreateTime(dateTime.toString("yyyy-MM-dd HH:mm:ss"));
+                    }
+                }
+                if (vDataObj.contains("roles")) {
+                    QJsonObject rolesJsonObj;
+                    rolesJsonObj["roles"] = vDataObj.value("roles");
+                    QJsonDocument rolesDoc(rolesJsonObj);
+                    UserInfoModel::instance()->setRoles(rolesDoc.toJson(QJsonDocument::Compact));
+                }
+                if (vDataObj.contains("dept")) {
+                    QJsonObject deptJsonObj;
+                    deptJsonObj["dept"] = vDataObj.value("dept");
+                    QJsonDocument deptDoc(deptJsonObj);
+                    UserInfoModel::instance()->setDept(deptDoc.toJson(QJsonDocument::Compact));
+                }
+                if (vDataObj.contains("posts")) {
+                    QJsonObject postsJsonObj;
+                    postsJsonObj["posts"] = vDataObj.value("posts");
+                    QJsonDocument postsDoc(postsJsonObj);
+                    UserInfoModel::instance()->setPosts(postsDoc.toJson(QJsonDocument::Compact));
+                }
+                emit signalUserInfoReturnStat(0, "已查询到用户信息");
+                return;
+            }
+        }
+    }
+    emit signalUserInfoReturnStat(-3, "无法查询到用户信息");
+    return;
+}

+ 26 - 0
src/httpclient/HttpGetUserInfo.h

@@ -0,0 +1,26 @@
+#ifndef HTTPGETUSERINFO_H
+#define HTTPGETUSERINFO_H
+#include <QThread>
+#include <QNetworkAccessManager>
+#include <QtQml/qqml.h>
+
+class HttpGetUserInfo: public QThread
+{
+    Q_OBJECT
+
+    QML_NAMED_ELEMENT(HttpGetUserInfo)
+public:
+    explicit HttpGetUserInfo(QObject *parent=nullptr);
+    ~HttpGetUserInfo();
+protected:
+    void run() override;
+    void cleanUserInfoModel();
+private:
+    void httpRequestGetUserInfo(void);
+public slots:
+    void slotHttpResponseGetUserInfo(QByteArray data);
+signals:
+    void signalGetRequestData(quint64 id, QString postUrl, QByteArray data, QString token);
+    void signalUserInfoReturnStat(int stat, const QString& msg);
+};
+#endif // HTTPGETUSERINFO_H

+ 72 - 0
src/httpclient/HttpUpdateUserInfo.cpp

@@ -0,0 +1,72 @@
+#include "HttpUpdateUserInfo.h"
+
+#include <QJsonObject>
+
+#include "config.h"
+#include "InteractiveData.h"
+
+HttpUpdateUserInfo::HttpUpdateUserInfo(QObject *parent)
+    : QThread(parent)
+{
+
+}
+
+void HttpUpdateUserInfo::run()
+{
+    httpRequestUpdateUserInfo();
+}
+
+void HttpUpdateUserInfo::httpRequestUpdateUserInfo()
+{
+    QJsonObject jsonRoot;
+    QDateTime currentDateTime = QDateTime::currentDateTime();
+    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();
+
+    QString url = Config()->updatePasswordUrl;
+    jsonRoot.insert("avatar", this->m_avatar);
+    jsonRoot.insert("email", this->m_email);
+    jsonRoot.insert("mobile", this->m_mobile);
+    jsonRoot.insert("nickname", this->m_nickname);
+    jsonRoot.insert("sex", this->m_sex);
+
+    QJsonDocument jsonDoc(jsonRoot);
+    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
+
+    emit signalPutRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
+}
+
+void HttpUpdateUserInfo::slotHttpResponseUpdateUserInfo(QByteArray data)
+{
+    QJsonParseError error;
+    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+    if (error.error != QJsonParseError::NoError) {
+        emit signalUpdateUserInfoReturnStat(-3, "更新用户信息失败");
+        return;
+    }
+    if(jsonDoc.isNull() || jsonDoc.isEmpty()) {
+        emit signalUpdateUserInfoReturnStat(-3, "更新用户信息失败");
+        return;
+    }
+
+    QJsonObject rootObj = jsonDoc.object();
+    if(rootObj.contains("code"))
+    {
+        int codeValue = rootObj.value("code").toInt();
+        if(codeValue == 200 || codeValue == 0) {
+            QString msg = "更新密码成功";
+            if (rootObj.contains("msg")) {
+                if (!rootObj.value("msg").toString().isEmpty()) {
+                    msg = rootObj.value("msg").toString();
+                }
+            }
+            emit signalUpdateUserInfoReturnStat(0, msg);
+        }
+        else {
+            QString msg = "更新密码失败";
+            if (rootObj.contains("msg")) {
+                msg = rootObj.value("msg").toString();
+            }
+            emit signalUpdateUserInfoReturnStat(-3, msg);
+        }
+    }
+}

+ 69 - 0
src/httpclient/HttpUpdateUserInfo.h

@@ -0,0 +1,69 @@
+#ifndef HTTPUPDATEUSERINFO_H
+#define HTTPUPDATEUSERINFO_H
+#include <QThread>
+#include <QtQml/qqml.h>
+
+class HttpUpdateUserInfo : public QThread
+{
+    Q_OBJECT
+    QML_NAMED_ELEMENT(HttpUpdateUserInfo)
+
+    Q_PROPERTY(QString nickname READ nickname WRITE setNickname);
+    Q_PROPERTY(QString email READ email WRITE setEmail);
+    Q_PROPERTY(QString mobile READ mobile WRITE setMobile);
+    Q_PROPERTY(int sex READ sex WRITE setSex);
+    Q_PROPERTY(QString avatar READ avatar WRITE setAvatar);
+public:
+    explicit HttpUpdateUserInfo(QObject *parent = nullptr);
+
+    QString nickname() {
+        return m_nickname;
+    }
+    void setNickname(const QString& nickname) {
+        m_nickname = nickname;
+    }
+
+    QString email() {
+        return m_email;
+    }
+    void setEmail(const QString& email) {
+        m_email = email;
+    }
+
+    QString mobile() {
+        return m_mobile;
+    }
+    void setMobile(const QString& mobile) {
+        m_mobile = mobile;
+    }
+
+    int sex() {
+        return m_sex;
+    }
+    void setSex(int sex) {
+        m_sex = sex;
+    }
+
+    QString avatar() {
+        return m_avatar;
+    }
+    void setAvatar(const QString& avatar) {
+        m_avatar = avatar;
+    }
+protected:
+    void run() override;
+private:
+    void httpRequestUpdateUserInfo(void);
+public slots:
+    void slotHttpResponseUpdateUserInfo(QByteArray data);
+signals:
+    void signalPutRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token);
+    void signalUpdateUserInfoReturnStat(int stat, QString str);
+private:
+    QString m_avatar;
+    QString m_email;
+    QString m_mobile;
+    QString m_nickname;
+    int m_sex;
+};
+#endif // HTTPUPDATEUSERINFO_H

+ 71 - 0
src/httpclient/HttpUpdateUserPassword.cpp

@@ -0,0 +1,71 @@
+#include "HttpUpdateUserPassword.h"
+
+#include <QJsonObject>
+
+#include "config.h"
+#include "InteractiveData.h"
+
+HttpUpdateUserPassword::HttpUpdateUserPassword(QObject *parent)
+    : QThread(parent)
+{
+
+}
+
+void HttpUpdateUserPassword::run()
+{
+    httpRequestUpdatePassword();
+}
+
+void HttpUpdateUserPassword::httpRequestUpdatePassword()
+{
+    QJsonObject jsonRoot;
+    QDateTime currentDateTime = QDateTime::currentDateTime();
+    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();
+
+    QString url = Config()->updatePasswordUrl;
+    jsonRoot.insert("oldPassword", this->m_oldPassword);
+    jsonRoot.insert("newPassword", this->m_newPassword);
+
+    QJsonDocument jsonDoc(jsonRoot);
+    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
+
+    emit signalPutRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
+}
+
+void HttpUpdateUserPassword::slotHttpResponseUpdatePassword(QByteArray data)
+{
+    QJsonParseError error;
+    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
+    if (error.error != QJsonParseError::NoError) {
+        emit signalUpdatePasswordReturnStat(-3, "更新密码失败");
+        return;
+    }
+    if(jsonDoc.isNull() || jsonDoc.isEmpty()) {
+        emit signalUpdatePasswordReturnStat(-3, "更新密码失败");
+        return;
+    }
+
+    QJsonObject rootObj = jsonDoc.object();
+    if(rootObj.contains("code"))
+    {
+        int codeValue = rootObj.value("code").toInt();
+        if(codeValue == 200 || codeValue == 0) {
+            QString msg = "更新密码成功";
+            if (rootObj.contains("msg")) {
+                if (!rootObj.value("msg").toString().isEmpty()) {
+                    msg = rootObj.value("msg").toString();
+                }
+            }
+            emit signalUpdatePasswordReturnStat(0, msg);
+        }
+        else {
+            QString msg = "更新密码失败";
+            if (rootObj.contains("msg")) {
+                if (!rootObj.value("msg").toString().isEmpty()) {
+                    msg = rootObj.value("msg").toString();
+                }
+            }
+            emit signalUpdatePasswordReturnStat(-3, msg);
+        }
+    }
+}

+ 43 - 0
src/httpclient/HttpUpdateUserPassword.h

@@ -0,0 +1,43 @@
+#ifndef HTTPUPDATEUSERPASSWORD_H
+#define HTTPUPDATEUSERPASSWORD_H
+#include <QThread>
+#include <QtQml/qqml.h>
+
+class HttpUpdateUserPassword : public QThread
+{
+    Q_OBJECT
+    QML_NAMED_ELEMENT(HttpUpdateUserPassword)
+
+    Q_PROPERTY(QString oldPassword READ oldPassword WRITE setOldPassword)
+    Q_PROPERTY(QString newPassword READ newPassword WRITE setNewPassword)
+public:
+    explicit HttpUpdateUserPassword(QObject *parent = nullptr);
+
+    QString oldPassword() {
+        return m_oldPassword;
+    }
+    void setOldPassword(const QString& oldPassword) {
+        m_oldPassword = oldPassword;
+    }
+
+    QString newPassword() {
+        return m_newPassword;
+    }
+    void setNewPassword(const QString& newPassword) {
+        m_newPassword = newPassword;
+    }
+protected:
+    void run() override;
+private:
+    void httpRequestUpdatePassword(void);
+public slots:
+    void slotHttpResponseUpdatePassword(QByteArray data);
+signals:
+    void signalPutRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token);
+    void signalUpdatePasswordReturnStat(int stat, QString str);
+private:
+    QString m_oldPassword;
+    QString m_newPassword;
+};
+
+#endif // HTTPUPDATEUSERPASSWORD_H

+ 2 - 2
src/httpclient/JobTicketsModel.h

@@ -13,8 +13,8 @@ public:
     QString m_sopIconCharacter = "\uf292";
     QString m_workstationIcon = "\uf3c5";
     QString m_timeIcon = "\uf1da";
-    QString m_workingStatusIcon = "\uf1da";
-    QString m_userIconCharacter = "\uf02b";
+    QString m_workingStatusIcon = "\uf02b";
+    QString m_userIconCharacter = "\uf406";
 
     quint16 m_workId;
     quint16 m_nodeId;

+ 27 - 0
src/httpclient/UserInfoModel.cpp

@@ -0,0 +1,27 @@
+#include "UserInfoModel.h"
+
+UserInfoModel* UserInfoModel::pInstance = nullptr;
+
+UserInfoModel::UserInfoModel(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+UserInfoModel::~UserInfoModel()
+{
+
+}
+
+UserInfoModel *UserInfoModel::instance()
+{
+    if (!pInstance) {
+        pInstance = new UserInfoModel(nullptr);
+    }
+    return pInstance;
+}
+
+UserInfoModel *UserInfoModel::create(QQmlEngine *, QJSEngine *)
+{
+    return instance();
+}

+ 147 - 0
src/httpclient/UserInfoModel.h

@@ -0,0 +1,147 @@
+#ifndef USERINFOMODEL_H
+#define USERINFOMODEL_H
+#include <QAbstractListModel>
+#include <QtQml/qqml.h>
+
+class UserInfoModel: public QObject
+{
+    Q_OBJECT
+    QML_SINGLETON
+    QML_NAMED_ELEMENT(UserInfoModel);
+
+    Q_PROPERTY(int id READ id WRITE setId);
+    Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY signalUsernameChanged);
+    Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY signalNicknameChanged);
+    Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY signalEmailChanged);
+    Q_PROPERTY(QString mobile READ mobile WRITE setMobile NOTIFY signalMobileChanged);
+    Q_PROPERTY(int sex READ sex WRITE setSex NOTIFY signalSexChanged);
+    Q_PROPERTY(QString avatar READ avatar WRITE setAvatar NOTIFY signalAvatarChanged);
+    Q_PROPERTY(QString loginIp READ loginIp WRITE setLoginIp);
+    Q_PROPERTY(QString loginDate READ loginDate WRITE setLoginDate);
+    Q_PROPERTY(QString createTime READ createTime WRITE setCreateTime);
+
+    Q_PROPERTY(QString roles READ roles WRITE setRoles);
+    Q_PROPERTY(QString dept READ dept WRITE setDept);
+    Q_PROPERTY(QString posts READ posts WRITE setPosts);
+private:
+    explicit UserInfoModel(QObject *parent=nullptr);
+    explicit UserInfoModel() = default;
+    ~UserInfoModel();
+public:
+    static UserInfoModel* pInstance;
+    static UserInfoModel* instance();
+    static UserInfoModel* create(QQmlEngine*, QJSEngine*);
+
+    int id() {
+        return m_id;
+    }
+    void setId(int id) {
+        m_id = id;
+    }
+
+    QString username() {
+        return m_username;
+    }
+    void setUsername(const QString& username) {
+        m_username = username;
+    }
+
+    QString nickname() {
+        return m_nickname;
+    }
+    void setNickname(const QString& nickname) {
+        m_nickname = nickname;
+    }
+
+    QString email() {
+        return m_email;
+    }
+    void setEmail(const QString& email) {
+        m_email = email;
+    }
+
+    QString mobile() {
+        return m_mobile;
+    }
+    void setMobile(const QString& mobile) {
+        m_mobile = mobile;
+    }
+
+    int sex() {
+        return m_sex;
+    }
+    void setSex(int sex) {
+        m_sex = sex;
+    }
+
+    QString avatar() {
+        return m_avatar;
+    }
+    void setAvatar(const QString& avatar) {
+        m_avatar = avatar;
+    }
+
+    QString loginIp() {
+        return m_loginIp;
+    }
+    void setLoginIp(const QString& loginIp) {
+        m_loginIp = loginIp;
+    }
+
+    QString loginDate() {
+        return m_loginDate;
+    }
+    void setLoginDate(const QString& loginDate) {
+        m_loginDate = loginDate;
+    }
+
+    QString createTime() {
+        return m_createTime;
+    }
+    void setCreateTime(const QString& createTime) {
+        m_createTime = createTime;
+    }
+
+    QString roles() {
+        return m_roles;
+    }
+    void setRoles(const QString& roles) {
+        m_roles = roles;
+    }
+
+    QString dept() {
+        return m_dept;
+    }
+    void setDept(const QString& dept) {
+        m_dept = dept;
+    }
+
+    QString posts() {
+        return m_posts;
+    }
+    void setPosts(const QString& posts) {
+        m_posts = posts;
+    }
+signals:
+    void signalUsernameChanged(const QString& username);
+    void signalNicknameChanged(const QString& nickname);
+    void signalMobileChanged(const QString& mobile);
+    void signalEmailChanged(const QString& email);
+    void signalSexChanged(int sex);
+    void signalAvatarChanged(const QString& avatar);
+private:
+    int m_id;
+    QString m_username;
+    QString m_nickname;
+    QString m_email;
+    QString m_mobile;
+    int m_sex;
+    QString m_avatar;
+    QString m_loginIp;
+    QString m_loginDate;
+    QString m_createTime;
+    QString m_roles;
+    QString m_dept;
+    QString m_posts;
+};
+#endif // USERINFOMODEL_H

+ 5 - 0
src/httpclient/WorkNodeFormModel.cpp

@@ -36,6 +36,11 @@ void WorkNodeFormModel::clear()
     endResetModel();
 }
 
+QString WorkNodeFormModel::getType(int index)
+{
+    return m_formList[index].m_type;
+}
+
 int WorkNodeFormModel::rowCount(const QModelIndex &parent) const
 {
     if (parent.isValid()) {

+ 2 - 0
src/httpclient/WorkNodeFormModel.h

@@ -66,6 +66,8 @@ public:
     void append(const FormControl& form);
     void clear();
 
+    Q_INVOKABLE QString getType(int index);
+
     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
     virtual QHash<int, QByteArray> roleNames() const override;
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

+ 53 - 5
src/main.qml

@@ -16,7 +16,7 @@ ApplicationWindow {
     flags: Qt.FramelessWindowHint       // 设置窗口无边框
 
     property int defaultLogoutSeconds: 120 + 1
-    property int startLogoutInterval: 25
+    property int startLogoutInterval: 10
 
     // 访问StackView
     default property alias appStackView: stackView
@@ -81,6 +81,54 @@ ApplicationWindow {
         }
     }
 
+    function showAlertDialog(msgTypeName, msgValue, msgIconChar, callbackFunc) {
+        var component = Qt.createComponent("qml/components/AlertDialog.qml");
+        if (component.status === Component.Ready) {
+            // 创建实例并添加到场景中
+            var alertDialogComp = component.createObject(appAlertDialog, {
+                                                             messageTypeName: msgTypeName,
+                                                             messageValue: msgValue,
+                                                             messageIconCharacter: msgIconChar
+                                                         });
+            alertDialogComp.parent = appAlertDialog;    // 将提示对话框挂载在appAlterDialog Loader加载器上
+            // 处理提示对话框的事件
+            alertDialogComp.confirm.connect(function () { callbackFunc(alertDialogComp); });
+        } else {
+            console.log("Error loading component:", component.errorString());
+        }
+    }
+
+    function negativeAlertDialogCallback(componentObj) {
+        componentObj.visible = false;
+        appAlertDialog.sourceComponent = null;
+        componentObj.destroy();
+    }
+
+    HttpGetUserInfo {
+        id: httpGetUserInfo
+
+        Component.onCompleted: {
+            httpGetUserInfo.signalGetRequestData.connect(httpClientThread.slotGetRequestData);
+            httpClientThread.signalResponseGetUserInfoUrl.connect(httpGetUserInfo.slotHttpResponseGetUserInfo);
+        }
+
+        Component.onDestruction: {
+            httpGetUserInfo.signalGetRequestData.disconnect(httpClientThread.slotGetRequestData);
+            httpClientThread.signalResponseGetUserInfoUrl.disconnect(httpGetUserInfo.slotHttpResponseGetUserInfo);
+        }
+    }
+
+    Connections {
+        target: httpGetUserInfo
+        function onSignalUserInfoReturnStat(stat, msg) {
+            if (stat !== 0) {
+                showAlertDialog("提示", msg, "\uf071", negativeAlertDialogCallback);
+            } else {
+                stackView.push("qml/SettingPage.qml");
+            }
+        }
+    }
+
     Item {
         id: contentWrapper
         anchors.fill: parent
@@ -149,7 +197,7 @@ ApplicationWindow {
 
                 text: qsTr("作业")
                 textColor: "white"
-                iconCharacter: "\uf013"
+                iconCharacter: "\uf0ae"
                 iconColor: "#40a9ff"
 
                 onClicked: {
@@ -167,11 +215,11 @@ ApplicationWindow {
                 iconColor: "#40a9ff"
 
                 onClicked: {
-                    stackView.push("qml/SettingPage.qml");
+                    httpGetUserInfo.start();
                 }
             }
             MButton {
-                Layout.preferredWidth: 154
+                Layout.preferredWidth: 180
                 Layout.preferredHeight: 52
                 buttonColor: "#33FF4D4F"
                 backgroundOpacity: 0.2
@@ -179,7 +227,7 @@ ApplicationWindow {
                 text: appLogoutSeconds % defaultLogoutSeconds === 0 ? qsTr(appLogoutText) :
                                                                       qsTr(appLogoutText + "(" + appLogoutSeconds.toString() + "秒)")
                 textColor: "white"
-                iconCharacter: "\uf013"
+                iconCharacter: "\uf2f5"
                 iconColor: "red"
 
                 onClicked: {

+ 2 - 2
src/qml/JobTicketPage.qml

@@ -127,8 +127,8 @@ Rectangle {
                             jobTitleText: name
                             sopIconCharacter: sopIconCharacter
                             sopInfoText: orderNo                        // 作业编号
-                            positionIconCharacter: workstationIcon
-                            positionText: ""                            // TODO: 暂时未获取到数据
+                            // positionIconCharacter: workstationIcon
+                            // positionText: ""                            // TODO: 暂时未获取到数据
                             workingStateCharacter: workingStatusIcon
                             workingStateText: approvalStatus
                             workingStateColor: approvalStatusColor

+ 139 - 120
src/qml/SettingPage.qml

@@ -2,6 +2,7 @@ import QtQuick
 import QtQuick.Layouts
 import QtQuick.Controls
 import "./components"
+import Loto
 
 Rectangle {
     id: control
@@ -15,12 +16,92 @@ Rectangle {
     property string userType: "操作员"
     property string userGroup: "电气维护组"
 
+    function showUpdatePasswordDialog(cancelCallbackFunc, confirmCallbackFunc) {
+        var component = Qt.createComponent("components/UpdatePasswordDialog.qml");
+        if (component.status === Component.Ready) {
+            // 创建实例并添加到场景中
+            var alertDialogComp = component.createObject(appAlertDialog);
+            alertDialogComp.parent = appAlertDialog;    // 将提示对话框挂载在appAlterDialog Loader加载器上
+            // 处理提示对话框的事件
+            alertDialogComp.cancel.connect(function () { cancelCallbackFunc(alertDialogComp); });
+            alertDialogComp.confirm.connect(function (oldValue, newValue) { confirmCallbackFunc(alertDialogComp, oldValue, newValue); });
+        } else {
+            console.log("Error loading component:", component.errorString());
+        }
+    }
+
+    function negativeDialogCallback(componentObj) {
+        componentObj.visible = false;
+        appAlertDialog.sourceComponent = null;
+        componentObj.destroy();
+    }
+
+    function opsitiveDialogCallbac(componentObj, oldValue, newValue) {
+        componentObj.visible = false;
+        httpUpdateUserPassword.oldPassword = oldValue;
+        httpUpdateUserPassword.newPassword = newValue;
+        httpUpdateUserPassword.start();
+        componentObj.destroy();
+    }
+
+    function showAlertDialog(msgTypeName, msgValue, msgIconChar, callbackFunc) {
+        var component = Qt.createComponent("components/AlertDialog.qml");
+        if (component.status === Component.Ready) {
+            // 创建实例并添加到场景中
+            var alertDialogComp = component.createObject(appAlertDialog, {
+                                                             messageTypeName: msgTypeName,
+                                                             messageValue: msgValue,
+                                                             messageIconCharacter: msgIconChar
+                                                         });
+            alertDialogComp.parent = appAlertDialog;    // 将提示对话框挂载在appAlterDialog Loader加载器上
+            // 处理提示对话框的事件
+            alertDialogComp.confirm.connect(function () { callbackFunc(alertDialogComp); });
+        } else {
+            console.log("Error loading component:", component.errorString());
+        }
+    }
+
+    function negativeAlertDialogCallback(componentObj) {
+        componentObj.visible = false;
+        appAlertDialog.sourceComponent = null;
+        componentObj.destroy();
+    }
+
+    function opsitiveAlertDialogCallback(componentObj) {
+        negativeAlertDialogCallback(componentObj);
+    }
+
+    HttpUpdateUserPassword {
+        id: httpUpdateUserPassword
+
+        Component.onCompleted: {
+            httpUpdateUserPassword.signalPutRequestData.connect(httpClientThread.slotPutRequestData);
+            httpClientThread.signalReponseUpdateUserPasswordUrl.connect(httpUpdateUserPassword.slotHttpResponseUpdatePassword);
+        }
+
+        Component.onDestruction: {
+            httpUpdateUserPassword.signalPutRequestData.disconnect(httpClientThread.slotPutRequestData);
+            httpClientThread.signalReponseUpdateUserPasswordUrl.disconnect(httpUpdateUserPassword.slotHttpResponseUpdatePassword);
+        }
+    }
+
+    Connections {
+        target: httpUpdateUserPassword
+        function onSignalUpdatePasswordReturnStat(stat, msg) {
+            if (stat !== 0) {
+                showAlertDialog("提示", msg, "\uf071", negativeAlertDialogCallback);
+            } else {
+                showAlertDialog("成功", msg, "\uf058", opsitiveAlertDialogCallback);
+            }
+        }
+    }
+
     Rectangle {
         id: __container
         x: 260
         y: 140
         width: 1400
-        height: 940
+        height: 935
 
         color: "#0A1929"
         radius: 16
@@ -44,7 +125,7 @@ Rectangle {
 
                 AvatarCard {
                     id: __userAvatar
-                    userName: userName
+                    usernameImage: UserInfoModel.avatar
                 }
 
                 Rectangle {
@@ -55,11 +136,12 @@ Rectangle {
                     Text {
                         id: __userNameLabel
                         anchors.top: parent.top
+                        anchors.topMargin: 10
                         anchors.left: parent.left
-                        text: qsTr(userName)
+                        text: qsTr(UserInfoModel.nickname)
                         opacity: 1
                         color: "white"
-                        font.pixelSize: 44
+                        font.pixelSize: 32
                         font.bold: true
                         verticalAlignment: Text.AlignVCenter
                         Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
@@ -69,27 +151,40 @@ Rectangle {
                         id: __userGroupBtn
                         height: 36
                         anchors.top: __userNameLabel.bottom
-                        anchors.topMargin: 10
+                        anchors.topMargin: 30
                         anchors.left: parent.left
 
                         buttonColor: "#1A40A9FF"
                         btnHoverEanabled: false
                         btnRadius: 10
 
-                        text: qsTr(userType)
+                        // text: qsTr(userType)
                         textColor: "#40A9FF"
 
                         cancelMouseArea: true
+
+                        Component.onCompleted: {
+                            var userGroupName;
+                            var roles = JSON.parse(UserInfoModel.roles)["roles"];
+                            for (var i = 0; i < roles.length; i++) {
+                                if (i === 0) {
+                                    userGroupName = roles[i].name;
+                                } else {
+                                    userGroupName = userGroupName + "," + roles[i].name;
+                                }
+                            }
+                            __userGroupBtn.text = qsTr(userGroupName);
+                        }
                     }
 
                     Text {
                         id: __emptyLabel
                         anchors.top: __userNameLabel.bottom
-                        anchors.topMargin: 10
+                        anchors.topMargin: 30
                         anchors.left: __userGroupBtn.right
                         anchors.leftMargin: 10
                         anchors.verticalCenter: __userGroupBtn.verticalCenter
-                        text: qsTr("用户: ")
+                        text: qsTr("公司/部门: ")
                         opacity: 1
                         color: "white"
                         font.pixelSize: 16
@@ -101,17 +196,21 @@ Rectangle {
                     Text {
                         id: __userGroupLabel
                         anchors.top: __userNameLabel.bottom
-                        anchors.topMargin: 10
+                        anchors.topMargin: 30
                         anchors.left: __emptyLabel.right
                         anchors.leftMargin: 0
                         anchors.verticalCenter: __userGroupBtn.verticalCenter
-                        text: qsTr(userGroup)
+                        // text: qsTr(userGroup)
                         opacity: 1
                         color: "#40A9FF"
                         font.pixelSize: 16
                         font.bold: true
                         verticalAlignment: Text.AlignVCenter
                         Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
+
+                        Component.onCompleted: {
+                            __userGroupLabel.text = qsTr(JSON.parse(UserInfoModel.dept)["dept"].name);
+                        }
                     }
                 }
             }
@@ -142,17 +241,16 @@ Rectangle {
             clip: true
 
             contentWidth: width
-            contentHeight: 1900
+            contentHeight: contentCol.implicitHeight
 
-            // TODO: 插槽,填充设置表单
             ColumnLayout {
                 id: contentCol
                 anchors.fill: parent
-                spacing: 20
+                spacing: 15
 
                 UserInfoCard {
                     id: user2
-                    height: 300
+                    height: 180
 
                     infoTypeName: "账号安全"
                     infoTypeNameIconCharactor: "\uf3ed"
@@ -162,29 +260,32 @@ Rectangle {
                         spacing: 10
 
                         SettingFormCard {
-                            id: setting3
+                            id: settingPassword
                             width: parent.width
 
+                            showUnderline: false
+
                             settingTypeName: "修改密码"
                             settingValue: "建议定期更换密码"
                             settingTypeNameIconCharactor: "\uf084"
 
                             iconColor: "#FFA500"
                             iconBgColor: "#1AFFA500"
-                        }
 
-                        MInput {
-                            id: message1
+                            MouseArea {
+                                anchors.fill: parent
 
-                            passwordVisible: true
-                            passwordField: true
+                                onClicked: {
+                                    showUpdatePasswordDialog(control.negativeDialogCallback, control.opsitiveDialogCallbac);
+                                }
+                            }
                         }
                     }
                 }
 
                 UserInfoCard {
                     id: user1
-                    height: 300
+                    height: 390
 
                     infoTypeName: "个人信息"
                     infoTypeNameIconCharactor: "\uf007"
@@ -194,120 +295,38 @@ Rectangle {
                         spacing: 10
 
                         SettingFormCard {
-                            id: setting1
-                            width: parent.width
-                        }
-                        SettingFormCard {
-                            id: setting2
+                            id: settingName
                             width: parent.width
 
-                            settingTypeName: "头像"
-                            settingValue: "点击更换头像"
-                            settingTypeNameIconCharactor: "\uf030"
+                            settingTypeName: "姓名"
+                            settingTypeNameIconCharactor: "\uf007"
+                            settingValue: UserInfoModel.nickname
                         }
-                    }
-                }
-
-                UserInfoCard {
-                    id: user3
-                    height: 300
-
-                    infoTypeName: "个人信息"
-                    infoTypeNameIconCharactor: "\uf007"
-
-                    content: ColumnLayout {
-                        width: parent.width
-                        spacing: 10
-
                         SettingFormCard {
-                            id: setting5
+                            id: settingPhone
                             width: parent.width
-                        }
-                        SettingFormCard {
-                            id: setting6
-                            width: parent.width
-
-                            settingTypeName: "头像"
-                            settingValue: "点击更换头像"
-                            settingTypeNameIconCharactor: "\uf030"
-                        }
-                    }
-                }
-
-                UserInfoCard {
-                    id: user4
-                    height: 300
-
-                    infoTypeName: "个人信息"
-                    infoTypeNameIconCharactor: "\uf007"
-
-                    content: ColumnLayout {
-                        width: parent.width
-                        spacing: 10
-
-                        SettingFormCard {
-                            id: setting7
-                            width: parent.width
-                        }
-                        SettingFormCard {
-                            id: setting8
-                            width: parent.width
-
-                            settingTypeName: "头像"
-                            settingValue: "点击更换头像"
-                            settingTypeNameIconCharactor: "\uf030"
-                        }
-                    }
-                }
 
-                UserInfoCard {
-                    id: user5
-                    height: 300
-
-                    infoTypeName: "个人信息"
-                    infoTypeNameIconCharactor: "\uf007"
-
-                    content: ColumnLayout {
-                        width: parent.width
-                        spacing: 10
-
-                        SettingFormCard {
-                            id: setting9
-                            width: parent.width
+                            settingTypeName: "手机号"
+                            settingValue: UserInfoModel.mobile
+                            settingTypeNameIconCharactor: "\uf3cd"
                         }
                         SettingFormCard {
-                            id: setting10
+                            id: settingEmail
                             width: parent.width
 
-                            settingTypeName: "头像"
-                            settingValue: "点击更换头像"
-                            settingTypeNameIconCharactor: "\uf030"
+                            settingTypeName: "邮箱"
+                            settingValue: UserInfoModel.email
+                            settingTypeNameIconCharactor: "\uf0e0"
                         }
-                    }
-                }
 
-                UserInfoCard {
-                    id: user6
-                    height: 300
-
-                    infoTypeName: "个人信息"
-                    infoTypeNameIconCharactor: "\uf007"
-
-                    content: ColumnLayout {
-                        width: parent.width
-                        spacing: 10
-
-                        SettingFormCard {
-                            id: setting11
-                            width: parent.width
-                        }
                         SettingFormCard {
-                            id: setting21
+                            id: settingSex
                             width: parent.width
 
-                            settingTypeName: "头像"
-                            settingValue: "点击更换头像"
-                            settingTypeNameIconCharactor: "\uf030"
+                            showUnderline: false
+                            settingTypeName: "性别"
+                            settingValue: UserInfoModel.sex === 0 ? "男" : "女"
+                            settingTypeNameIconCharactor: "\uf228"
                         }
                     }
                 }

+ 12 - 12
src/qml/WorkingPage.qml

@@ -19,8 +19,8 @@ Rectangle {
     property string titleText: "审核作业"
     property string sopIconCharacter: '\uf292'
     property string sopInfoText: "WO-2024-001"
-    property string positionIconCharacter: "\uf3c5"
-    property string positionText: "车间A岗位"
+    // property string positionIconCharacter: "\uf3c5"
+    // property string positionText: "车间A岗位"
     property string userIconCharacter: "\uf406"
     property string userName: "张三"
     property string workingStateCharacter: "\uf02b"
@@ -73,7 +73,7 @@ Rectangle {
         x: 260
         y: 140
         width: 1400
-        height: 930
+        height: 935
 
         color: "#0A1929"
         radius: 16
@@ -115,7 +115,7 @@ Rectangle {
                     id: workingTitleBtn
                     visible: titleText !== ""
 
-                    iconCharacter: titleIconCharacter
+                    // iconCharacter: titleIconCharacter
                     iconSize: titleIconCharacterSize
                     text: titleText
                     textColor: "white"
@@ -131,15 +131,15 @@ Rectangle {
                     textColor: "white"
                 }
 
-                IconText {
-                    id: workingPositonBtn
-                    visible: positionText !== ""
+                // IconText {
+                //     id: workingPositonBtn
+                //     visible: positionText !== ""
 
-                    iconCharacter: positionIconCharacter
-                    iconSize: 14
-                    text: positionText
-                    textColor: "white"
-                }
+                //     iconCharacter: positionIconCharacter
+                //     iconSize: 14
+                //     text: positionText
+                //     textColor: "white"
+                // }
 
                 IconText {
                     id: workingUserBtn

+ 50 - 10
src/qml/components/AvatarCard.qml

@@ -12,22 +12,62 @@ Rectangle {
     color: "#40a9ff"
 
     // API
+    property string usernameImage
     property string userName: "李明"
     property string iconCharacter: "\uf030"
     property color iconColor: "#40a9ff"
     property int iconSize: 14
 
-    Text {
-        id: __userName
+    // Text {
+    //     id: __userName
+    //     anchors.centerIn: parent
+    //     text: qsTr(userName.substring(0, 1))
+    //     opacity: 1
+    //     color: "white"
+    //     font.pixelSize: 48
+    //     font.bold: true
+    //     horizontalAlignment: Text.horizontalAlignment
+    //     verticalAlignment: Text.AlignVCenter
+    //     Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
+    // }
+    // 原始图像,隐藏
+    Image {
+        id: sourceItem
+        source: control.usernameImage
         anchors.centerIn: parent
-        text: qsTr(userName.substring(0, 1))
-        opacity: 1
-        color: "white"
-        font.pixelSize: 48
-        font.bold: true
-        horizontalAlignment: Text.horizontalAlignment
-        verticalAlignment: Text.AlignVCenter
-        Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
+        width: control.width
+        height: control.height
+        fillMode: Image.PreserveAspectCrop
+        visible: false
+    }
+
+    //
+    MultiEffect {
+        id: multiEffect
+        source: sourceItem
+        anchors.fill: sourceItem
+        maskEnabled: true
+        maskSource: mask
+        // 下面两个属性抗锯齿
+        maskThresholdMin: 0.5
+        maskSpreadAtMin: 1.0
+
+    }
+
+    // 圆形黑色矩形(用于遮罩)
+    Item {
+        id: mask
+        width: sourceItem.width
+        height: sourceItem.height
+        layer.enabled: true
+        visible: false
+
+
+        Rectangle {
+            anchors.fill: parent
+            radius: width / 2
+            color: "black" // 黑色用于掩码:纯黑表示完全不透明
+        }
     }
 
     Rectangle {

+ 38 - 41
src/qml/components/FormCard.qml

@@ -23,41 +23,14 @@ Rectangle {
     property int padding: 10    // 边距
     property int spacing: 16    // 间隔
     property int itemWidth: 300
-    property int itemHeight: 144
+    // property int itemHeight: 144
+    property int itemHeight: 74
     property int delegateWidth: itemWidth + spacing
     property int delegateHeight: itemHeight + spacing
 
     property int textAreaHeight: 160
     property var returnFormInfo
 
-    // IconText {
-    //     id: __title
-    //     visible: currentTitle !== ""
-    //     anchors.left: parent.left
-    //     anchors.leftMargin: 18
-    //     anchors.rightMargin: 18
-    //     anchors.top: parent.top
-
-    //     iconCharacter: currentTitleCharacter
-    //     iconSize: control.iconSize
-    //     text: currentTitle
-    //     textColor: control.textColor
-
-    //     Canvas {
-    //         id: __workingInfoCanvas
-    //         anchors.fill: parent
-    //         onPaint: {
-    //             var ctx = getContext("2d");
-    //             ctx.beginPath();
-    //             ctx.lineWidth = 1;                              // 边框宽度
-    //             ctx.moveTo(0, parent.height);                   // 移动到起始点
-    //             ctx.lineTo(control.width, parent.height);        // 画线到另一个点
-    //             ctx.strokeStyle = "#40A9FF";                    // 边框颜色
-    //             ctx.stroke();
-    //         }
-    //     }
-    // }
-
     Flickable {
         id: contentItem
         width: parent.width-45
@@ -71,14 +44,12 @@ Rectangle {
 
         ListView {
             id: list_view
-            // anchors.fill: parent
             width: parent.width
             height: parent.height+control.delegateHeight+control.spacing
 
             flickableDirection: Flickable.HorizontalAndVerticalFlick
             boundsBehavior: Flickable.StopAtBounds
             headerPositioning: ListView.OverlayHeader
-            // footerPositioning: ListView.OverlayFooter
             clip: true
             focus: true
 
@@ -92,9 +63,8 @@ Rectangle {
 
             delegate: Item {
                 id: formItem
-                width: control.delegateWidth
-                // height:control.delegateHeight
-                height: ((groupIndex%(gridColumns===0?1:gridColumns))===0) ? control.delegateHeight : 0
+                // 每行第一个Item有高度,后面的没高度,这样就能排列到一行
+                height: ((groupIndex%(gridColumns===0?1:gridColumns))===0) ? control.getDelegateHeight(index, gridColumns, groupIndex) : 0
 
                 Rectangle {
                     id: formArea
@@ -133,11 +103,9 @@ Rectangle {
 
                     Loader {
                         id: formControl
-                        // x: formLabel.x + formLabel.width + control.padding
                         anchors.top: formLabel.bottom
                         anchors.topMargin: 8
                         width: formArea.width
-                        // height: formArea.height - control.padding*2
                         anchors.verticalCenter: parent.verticalCenter
                     }
                 }
@@ -147,14 +115,14 @@ Rectangle {
                     var columns = gridColumns === 0 ? 1 : gridColumns;
 
                     formArea.x = gridColumns === 0 ? 0 : (groupIndex % columns) * (contentItem.width/columns);
-                    formArea.y = groupIndex % columns !== 0 ? -control.delegateHeight : 0
+                    // 当前一行存在类似TextArea与Input等控件高度不一致的情况发生时,重新计算前一行的最大高度和当前行Y位置
+                    formArea.y = groupIndex % columns !== 0 ? -control.getDelegateHeight(index, gridColumns, groupIndex) : control.getDelegateY(index, gridColumns, groupIndex);
                     if (gridColumns === 0) {
                         formArea.width = contentItem.width - control.spacing;
                     } else {
                         formArea.width = contentItem.width/columns - control.spacing;
                     }
-
-                    formArea.height = control.delegateHeight - control.spacing
+                    formArea.height = control.getDelegateHeight(index, gridColumns, groupIndex) - control.spacing
 
                     formLabel.text = qsTr(label);
 
@@ -223,9 +191,8 @@ Rectangle {
                         formControl.item.placeholderText = placeholder;
                         formControl.item.required = required;
                         formControl.item.requiredMsg = requiredMessage !== "" ? requiredMessage : label.trim()+"不能为空";
-                        formControl.item.height = formArea.height - 30
 
-                        // formArea.height = control.textAreaHeight;
+                        formArea.height = control.textAreaHeight;
 
                         formControl.item.signalTextAreaChanged.connect(control.slotCollectInputInfo);
                     } else {
@@ -238,6 +205,36 @@ Rectangle {
         }
     }
 
+    // 计算ListView中代理项的Y位置
+    function getDelegateY(index, gridColumns, groupIndex) {
+        var delegateY = 0;
+        if (index-gridColumns >= 0 && (groupIndex%(gridColumns===0?1:gridColumns))===0) {
+            for (var i = index-gridColumns; i < index; i++) {
+                var itemType = WorkNodeFormModel.getType(i);
+
+                if (itemType === "textarea") {
+                    delegateY = control.textAreaHeight-control.itemHeight-control.spacing;
+                }
+            }
+        }
+        return delegateY;
+    }
+
+    // 计算ListView中某行代理项的最大高度
+    function getDelegateHeight(index, gridColumns, groupIndex) {
+        var delegateHeight = control.delegateHeight;
+        if (index-gridColumns >= 0 && (groupIndex%(gridColumns===0?1:gridColumns))===0) {
+            for (var i = index-gridColumns; i < index; i++) {
+                var itemType = WorkNodeFormModel.getType(i);
+
+                if (itemType === "textarea") {
+                    delegateHeight = control.textAreaHeight;
+                }
+            }
+        }
+        return delegateHeight;
+    }
+
     function slotCollectInputInfo(index, id, data) {
         var formInfo = control.returnFormInfo["fields"][index];
         if (typeof(formInfo) === "string") {

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

@@ -18,8 +18,8 @@ Rectangle {
     property string jobTitleText: ""
     property string sopIconCharacter: '\uf292'
     property string sopInfoText: "WO-2024-001"
-    property string positionIconCharacter: "\uf3c5"
-    property string positionText: "车间A岗位"
+    // property string positionIconCharacter: "\uf3c5"
+    // property string positionText: "车间A岗位"
     property string userIconCharacter: "\uf406"
     property string userName: "张三"
     property string workingStateCharacter: "\uf02b"
@@ -127,8 +127,8 @@ Rectangle {
                                     titleText: jobTitleText,
                                     sopIconCharacter: sopIconCharacter,
                                     sopInfoText: sopInfoText,
-                                    positionIconCharacter: positionIconCharacter,
-                                    positionText: positionText,
+                                    // positionIconCharacter: positionIconCharacter,
+                                    // positionText: positionText,
                                     workingStateCharacter: workingStateCharacter,
                                     workingStateText: workingStateText,
                                     workingTimeCharacter: workingTimeCharacter,
@@ -156,7 +156,7 @@ Rectangle {
         anchors.left: parent.left
         anchors.leftMargin: 35
 
-        iconCharacter: jobTitleIconCharacter
+        // iconCharacter: jobTitleIconCharacter
         text: jobTitleText
         textColor: cardTextColor
         iconSize: 24
@@ -186,7 +186,7 @@ Rectangle {
         height: 25
 
         anchors.top: __titleLabel.bottom
-        anchors.topMargin: 15
+        anchors.topMargin: 30
         anchors.left: parent.left
         anchors.leftMargin: 35
 
@@ -196,32 +196,32 @@ Rectangle {
         iconSize: 16
     }
 
-    IconText {
-        id: __jobPosition
-        height: 25
+    // IconText {
+    //     id: __jobPosition
+    //     height: 25
 
-        anchors.top: __jobSop.bottom
-        anchors.topMargin: 10
-        anchors.left: parent.left
-        anchors.leftMargin: 35
+    //     anchors.top: __jobSop.bottom
+    //     anchors.topMargin: 15
+    //     anchors.left: parent.left
+    //     anchors.leftMargin: 35
 
-        iconCharacter: positionIconCharacter
-        text: "车间岗位:    " + positionText
-        textColor: cardTextColor
-        iconSize: 16
-    }
+    //     iconCharacter: positionIconCharacter
+    //     text: "车间岗位:    " + positionText
+    //     textColor: cardTextColor
+    //     iconSize: 16
+    // }
 
     IconText {
         id: __jobUser
         height: 25
 
-        anchors.top: __jobPosition.bottom
-        anchors.topMargin: 10
+        anchors.top: __jobSop.bottom
+        anchors.topMargin: 18
         anchors.left: parent.left
         anchors.leftMargin: 35
 
         iconCharacter: userIconCharacter
-        text: "负责人:    " + userName
+        text: "执行人:    " + userName
         textColor: cardTextColor
         iconSize: 16
     }
@@ -231,7 +231,7 @@ Rectangle {
         height: 25
 
         anchors.top: __jobUser.bottom
-        anchors.topMargin: 10
+        anchors.topMargin: 18
         anchors.left: parent.left
         anchors.leftMargin: 35
 
@@ -246,11 +246,11 @@ Rectangle {
         height: 25
 
         anchors.top: __jobStartTime.bottom
-        anchors.topMargin: 10
+        anchors.topMargin: 18
         anchors.left: parent.left
         anchors.leftMargin: 35
 
-        text: "作业任务"
+        text: "当前任务: "
         textColor: cardTextColor
         iconSize: 16
     }
@@ -260,9 +260,9 @@ Rectangle {
         height: 30
 
         anchors.top: __jobStartTime.bottom
-        anchors.topMargin: 10
+        anchors.topMargin: 18
         anchors.left: __jobWorkingLabel.right
-        anchors.leftMargin: 20
+        anchors.leftMargin: 10
 
         buttonColor: "#1A40A9FF"
         btnHoverEanabled: false

+ 99 - 11
src/qml/components/JobTicketProcess.qml

@@ -14,6 +14,8 @@ Rectangle {
     property string jobTicketStatusTypeName: "正在弹出锁具"
     property string jobTicketStatusIconCharactor: "\uf007"
     property string jobTicketStatusValue: "正在弹出锁具>>"
+    property int lockStatusCardWidth: 169
+    property int lockStatusCardHeight: 226
 
     // 上锁
     property var lockModel: ListModel {
@@ -27,6 +29,12 @@ Rectangle {
         ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "王五"; colocked: false }
         ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "王五"; colocked: false }
         ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "王五"; colocked: false }
+        ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "张三"; colocked: false }
+        ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "李四"; colocked: false }
+        ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "王五"; colocked: false }
+        ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "张三"; colocked: false }
+        ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "李四"; colocked: false }
+        ListElement { lockStatus: "未共锁"; lockStatusIcon: "\uf09c"; name: "王五"; colocked: false }
     }
 
     property int fontSize: 16
@@ -250,7 +258,7 @@ Rectangle {
                 anchors.horizontalCenter: parent.horizontalCenter
                 anchors.top: __lockTips.bottom
                 anchors.topMargin: 135
-                width: Math.min(__lockRow.width, 798)
+                width: Math.min(__lockRow.width, control.lockStatusCardWidth*4+45)
                 height: parent.height - __lockTips.height - 75
                 clip: true
                 flickableDirection: Flickable.HorizontalFlick
@@ -265,8 +273,8 @@ Rectangle {
                     Repeater {
                         model: control.lockModel
                         delegate: LockStatusCard {
-                            width: 169
-                            height: 226
+                            width: control.lockStatusCardWidth
+                            height: control.lockStatusCardHeight
                             bgRadius: 8
 
                             cardColor: "transparent"
@@ -295,6 +303,86 @@ Rectangle {
                     }
                 }
             }
+
+            Rectangle {
+               id: __lockLeftButton
+               width: 50; height: 50
+               anchors.left: __lockView.left
+               anchors.leftMargin: -55
+               anchors.verticalCenter: __lockView.verticalCenter
+               color: 'transparent'
+               border.color: "#40A9FF"
+               radius: width/2
+
+               visible: __lockView.contentX === 0 ? false : true
+
+               Text {
+                   id: __lockIconLabel
+                   anchors.centerIn: parent
+                   text: "\uf104"
+                   color: "#40A9FF"
+                   font.pixelSize: 36
+                   font.family: iconFont.name
+                   verticalAlignment: Text.AlignVCenter
+                   horizontalAlignment: Text.AlignHCenter
+               }
+
+               MouseArea {
+                   anchors.fill: parent
+
+                   onClicked: {
+                       if (__lockView.contentX > 0) {
+                           if (__lockView.contentX > __lockView.width+15) {
+                                __lockView.contentX -= __lockView.width+15;  // 向左滚动一个页面宽度
+                           } else {
+                               __lockView.contentX = 0;
+                           }
+                       } else {
+                           __lockView.contentX = 0;  // 如果已经到达最左侧,则不滚动
+                       }
+                   }
+               }
+           }
+
+            Rectangle {
+                id: __lockRightButton
+                width: 50; height: 50
+                anchors.right: __lockView.right
+                anchors.rightMargin: -55
+                anchors.verticalCenter: __lockView.verticalCenter
+                color: 'transparent'
+                border.color: "#40A9FF"
+                radius: width/2
+
+                visible: __lockView.contentX === __lockView.contentWidth - __lockView.width ? false : true
+
+                Text {
+                    id: __lockRIconLabel
+                    anchors.centerIn: parent
+                    text: "\uf105"
+                    color: "#40A9FF"
+                    font.pixelSize: 36
+                    font.family: iconFont.name
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                }
+
+                MouseArea {
+                    anchors.fill: parent
+
+                    onClicked: {
+                        if (__lockView.contentX < __lockView.contentWidth - __lockView.width) {
+                            if ((__lockView.contentX+__lockView.width+15) >= __lockView.contentWidth - __lockView.width) {
+                                __lockView.contentX = __lockView.contentWidth - __lockView.width;
+                            } else {
+                                __lockView.contentX += __lockView.width+15;  // 向右滚动一个页面宽度
+                            }
+                        } else {
+                            __lockView.contentX = __lockView.contentWidth - __lockView.width;  // 如果已经到达最右侧,则不滚动超过内容宽度
+                        }
+                    }
+                }
+            }
         }
         Rectangle {
             width: stackLayout.width
@@ -322,7 +410,7 @@ Rectangle {
                 anchors.horizontalCenter: parent.horizontalCenter
                 anchors.top: __colockTips.bottom
                 anchors.topMargin: 135
-                width: Math.min(__colockRow.width, 798)
+                width: Math.min(__colockRow.width, control.lockStatusCardWidth*4 + 45)
                 height: __colockRow.height + 30
 
                 clip: true
@@ -338,8 +426,8 @@ Rectangle {
                     Repeater {
                         model: control.colockModel
                         delegate: LockStatusCard {
-                            width: 169
-                            height: 226
+                            width: control.lockStatusCardWidth
+                            height: control.lockStatusCardHeight
                             bgRadius: 8
 
                             cardColor: "transparent"
@@ -397,8 +485,8 @@ Rectangle {
 
                    onClicked: {
                        if (__colockView.contentX > 0) {
-                           if (__colockView.contentX > __colockView.width / 3) {
-                                __colockView.contentX -= __colockView.width / 3;  // 向左滚动一个页面宽度
+                           if (__colockView.contentX > __colockView.width+15) {
+                                __colockView.contentX -= __colockView.width+15;  // 向左滚动一个页面宽度
                            } else {
                                __colockView.contentX = 0;
                            }
@@ -437,10 +525,10 @@ Rectangle {
 
                     onClicked: {
                         if (__colockView.contentX < __colockView.contentWidth - __colockView.width) {
-                            if (__colockView.contentX + __colockView.width / 3 < __colockView.contentWidth) {
-                                __colockView.contentX += __colockView.width / 3;  // 向右滚动一个页面宽度
+                            if ((__colockView.contentX+__colockView.width+15) >= __colockView.contentWidth - __colockView.width) {
+                                __colockView.contentX = __colockView.contentWidth - __colockView.width;
                             } else {
-                                __colockView.contentX = __colockView.contentWidth;
+                                __colockView.contentX += __colockView.width+15;  // 向右滚动一个页面宽度
                             }
                         } else {
                             __colockView.contentX = __colockView.contentWidth - __colockView.width;  // 如果已经到达最右侧,则不滚动超过内容宽度

+ 2 - 0
src/qml/components/JobTicketSubProcess.qml

@@ -62,6 +62,7 @@ Rectangle {
             font.pixelSize: 18
             font.family: iconFont.name
             font.bold: true
+            font.weight: 800
             verticalAlignment: Text.AlignVCenter
             horizontalAlignment: Text.AlignHCenter
         }
@@ -74,6 +75,7 @@ Rectangle {
             color: "#b3c1d1"
             font.pixelSize: 16
             font.bold: true
+            font.weight: 800
             font.family: iconFont.name
             verticalAlignment: Text.AlignVCenter
             horizontalAlignment: Text.AlignHCenter

+ 24 - 12
src/qml/components/LockStatusCard.qml

@@ -44,24 +44,36 @@ Rectangle {
     border.color: bgBorderColor
 
     // === 内容布局 ===
-    Text {
-        id: iconLabel
+    // Text {
+    //     id: iconLabel
+    //     anchors.top: parent.top
+    //     anchors.topMargin: 10
+    //     anchors.horizontalCenter: parent.horizontalCenter
+    //     text: control.iconCharacter
+    //     visible: control.iconCharacter !== ""
+    //     color: control.iconColor
+    //     font.pixelSize: control.iconSize
+    //     font.family: control.iconFontFamily
+    //     verticalAlignment: Text.AlignVCenter
+    //     horizontalAlignment: Text.AlignHCenter
+    // }
+    Rectangle {
+        id: userImageArea
+        width: 60
+        height: 60
         anchors.top: parent.top
-        anchors.topMargin: 10
+        anchors.topMargin: 30
         anchors.horizontalCenter: parent.horizontalCenter
-        text: control.iconCharacter
-        visible: control.iconCharacter !== ""
-        color: control.iconColor
-        font.pixelSize: control.iconSize
-        font.family: control.iconFontFamily
-        verticalAlignment: Text.AlignVCenter
-        horizontalAlignment: Text.AlignHCenter
+        color: "transparent"
+        border.color: "green"
+
+        radius: width/2
     }
 
     Text {
         id: label
-        anchors.top: iconLabel.bottom
-        anchors.topMargin: 10
+        anchors.top: userImageArea.bottom
+        anchors.topMargin: 30
         anchors.horizontalCenter: parent.horizontalCenter
         height: 30
         text: qsTr(control.text)

+ 2 - 1
src/qml/components/MDatePicker.qml

@@ -90,6 +90,7 @@ Item {
 
         Text {
             id: popupSymbo
+            Layout.rightMargin: 8
             text: showDatePopupSymbo
             color: "#666"
             font.pixelSize: 16
@@ -135,7 +136,7 @@ Item {
         id: textRequiredMsg
         anchors.top: layout.bottom
         anchors.topMargin: 15
-        anchors.right: layout.right
+        anchors.left: layout.left
         visible: control.required && control.showRequiredMsg
         text: qsTr(requiredMsg)
         color: "red"

+ 8 - 7
src/qml/components/MInput.qml

@@ -27,6 +27,7 @@ Item {
     property string showPasswordSymbol: "👁"
     property string hidePasswordSymbol: "🙈"
     property color textColor: "white"
+    property color bgBorderColor: "#40A9FF"
 
     // === 状态属性 ===
     property bool enabled: true
@@ -52,7 +53,7 @@ Item {
         // border.color: control.backgroundVisible
         //                ? theme.getBorderColor(textField.activeFocus)
         //                : (textField.activeFocus ? theme.focusColor : "#40A9FF")
-        border.color: "#40A9FF"
+        border.color: control.bgBorderColor
         border.width: 1
         // color: control.backgroundVisible ? theme.secondaryColor : "transparent"
         color: "transparent"
@@ -85,11 +86,11 @@ Item {
             background: null
             onAccepted: control.accepted()
 
-            onFocusChanged: {
-                if (!focus) {
-                    forceActiveFocus()
-                }
-            }
+            // onFocusChanged: {
+            //     if (!focus) {
+            //         forceActiveFocus()
+            //     }
+            // }
 
             onTextChanged: signalTextChanged(control.modelIndex, control.controlId, textField.text);
         }
@@ -117,7 +118,7 @@ Item {
         id: textRequiredMsg
         anchors.top: layout.bottom
         anchors.topMargin: 15
-        anchors.right: layout.right
+        anchors.left: layout.left
         visible: control.required && control.showRequiredMsg
         text: qsTr(control.requiredMsg)
         color: "red"

+ 1 - 1
src/qml/components/MRadioButton.qml

@@ -226,7 +226,7 @@ Rectangle {
         id: textRequiredMsg
         anchors.top: buttonsRow.bottom
         anchors.topMargin: 15
-        anchors.right: buttonsRow.right
+        anchors.left: buttonsRow.left
         visible: control.required && control.showRequiredMsg
         text: qsTr(requiredMsg)
         color: "red"

+ 1 - 1
src/qml/components/MSwitchButton.qml

@@ -158,7 +158,7 @@ Rectangle {
         id: textRequiredMsg
         anchors.top: layout.bottom
         anchors.topMargin: 15
-        anchors.right: layout.right
+        anchors.left: layout.left
         visible: control.required && control.showRequiredMsg
         text: qsTr(requiredMsg)
         color: "red"

+ 2 - 1
src/qml/components/MTextArea.qml

@@ -81,6 +81,7 @@ Item {
             TextArea {
                 id: textArea
                 width: textScroll.width
+                height: textScroll.height
                 wrapMode: Text.Wrap
                 font.pixelSize: control.fontSize
                 placeholderText: "请输入内容"
@@ -107,7 +108,7 @@ Item {
         id: textRequiredMsg
         anchors.top: contentLayout.bottom
         anchors.topMargin: 15
-        anchors.right: contentLayout.right
+        anchors.left: contentLayout.left
         visible: control.required && control.showRequiredMsg
         text: qsTr(requiredMsg)
         color: "red"

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

@@ -3,7 +3,7 @@ import QtQuick
 Rectangle {
     id: control
     width: parent.width
-    height: __iconArea.height * 1.5
+    height: __iconArea.height * 1.3
 
     color: "transparent"
     radius: 20
@@ -15,9 +15,11 @@ Rectangle {
     property color iconColor: "#40A9FF"
     property color iconBgColor: "#1A40A9FF"
     property int iconSize: 24
+    property bool showUnderline: true
 
     Canvas {
         id: __canvas
+        visible: control.showUnderline
         anchors.fill: parent
         onPaint: {
             var ctx = getContext("2d");
@@ -64,10 +66,11 @@ Rectangle {
             id: __settingKey
             anchors.top: parent.top
             anchors.topMargin: 0
-            text: settingTypeName
+            text: qsTr(settingTypeName)
             visible: settingTypeName !== ""
             color: "white"
-            font.pixelSize: iconSize * 0.75
+            font.pixelSize: 18
+            font.bold: true
             font.family: iconFont.name
             verticalAlignment: Text.AlignVCenter
             horizontalAlignment: Text.AlignHCenter
@@ -76,11 +79,11 @@ Rectangle {
             id: __settingValue
             anchors.top: __settingKey.bottom
             anchors.topMargin: 5
-            text: settingValue
+            text: qsTr(settingValue)
             visible: settingValue !== ""
-            color: "white"
-            font.pixelSize: iconSize * 0.75
-            font.family: iconFont.name
+            color: "#B3C1D1"
+            font.pixelSize: 16
+            // font.family: iconFont.name
             verticalAlignment: Text.AlignVCenter
             horizontalAlignment: Text.AlignHCenter
         }

+ 322 - 0
src/qml/components/UpdatePasswordDialog.qml

@@ -0,0 +1,322 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Rectangle {
+    id: control
+
+    anchors.fill: parent
+
+    // API
+
+    signal confirm(string oldPassword, string newPassword)
+    signal cancel()
+
+    MBlurCard {
+        id: __blurCard
+        anchors.fill: parent
+        blurSource: appBlurItem
+        blurAmount: 1.2
+        blurMax: 32
+        z: 1000
+
+        opacity: 1
+        scale: 1.0
+
+        Rectangle {
+            id: __dialogArea
+            width: 800
+            height: 500
+            anchors.centerIn: parent
+
+            color: "#0A1929"
+            opacity: 0.85
+            radius: 16
+            border.color: "#40A9FF"
+
+            Rectangle {
+                id: __titleArea
+                anchors.top: parent.top
+                anchors.topMargin: 40
+                anchors.left: parent.left
+                anchors.leftMargin: 50
+                width: 697
+                height:  54
+
+                color: "transparent"
+
+                Canvas {
+                    id: __canvas
+                    anchors.fill: parent
+                    onPaint: {
+                        var ctx = getContext("2d");
+                        ctx.beginPath();
+                        ctx.lineWidth = 1;                              // 边框宽度
+                        ctx.moveTo(0, parent.height);                   // 移动到起始点
+                        ctx.lineTo(parent.width, parent.height);        // 画线到另一个点
+                        ctx.strokeStyle = "#40A9FF";                    // 边框颜色
+                        ctx.stroke();
+                    }
+                }
+
+                IconText {
+                    id: __titleLabel
+                    anchors.left: parent.left
+                    anchors.verticalCenter: parent.verticalCenter
+
+                    iconCharacter: "\uf084"
+                    text: qsTr("修改密码")
+                    textColor: iconColor
+                    iconSize: 28
+                }
+            }
+
+            Rectangle {
+                id: oldPasswordArea
+                anchors.top: __titleArea.bottom
+                anchors.topMargin: 25
+                anchors.left: parent.left
+                anchors.leftMargin: 50
+                width: 697
+                height: 66
+
+                color: "transparent"
+                // border.color: "#40A9FF"
+                radius: 12
+
+                Text {
+                    id: oldPasswordLabel
+                    anchors.top: parent.top
+                    anchors.topMargin: 0
+                    anchors.left: parent.left
+                    anchors.leftMargin: 0
+                    // width: 90
+                    height: 21
+
+                    text: qsTr("当前密码")
+                    color: "#B3C1D1"
+                    font.pixelSize: 18
+                    font.family: iconFont.name
+                    font.bold: true
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                }
+
+                MInput {
+                    id: oldPasswordInput
+                    anchors.top: oldPasswordLabel.bottom
+                    anchors.topMargin: 5
+                    width: 697
+                    height: 40
+
+                    bgBorderColor: "#C340A9FF"
+                    required: true
+                    requiredMsg: "请输入旧密码"
+
+                    placeholderText: "请输入当前密码"
+                    textColor: "#B3C1D1"
+
+                    passwordField: true
+                }
+            }
+
+            Rectangle {
+                id: newPasswordArea
+                anchors.top: oldPasswordArea.bottom
+                anchors.topMargin: 20
+                anchors.left: parent.left
+                anchors.leftMargin: 50
+                width: 697
+                height: 66
+
+                color: "transparent"
+                // border.color: "#40A9FF"
+                radius: 12
+
+                Text {
+                    id: newPasswordLabel
+                    anchors.top: parent.top
+                    anchors.topMargin: 0
+                    anchors.left: parent.left
+                    anchors.leftMargin: 0
+                    // width: 90
+                    height: 21
+
+                    text: qsTr("新密码")
+                    color: "#B3C1D1"
+                    font.pixelSize: 18
+                    font.family: iconFont.name
+                    font.bold: true
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                }
+
+                MInput {
+                    id: newPasswordInput
+                    anchors.top: newPasswordLabel.bottom
+                    anchors.topMargin: 5
+                    width: 697
+                    height: 40
+
+                    bgBorderColor: "#C340A9FF"
+                    required: true
+                    requiredMsg: "请输入新密码"
+
+                    placeholderText: "请输入新密码"
+                    textColor: "#B3C1D1"
+
+                    passwordField: true
+                }
+            }
+
+            Rectangle {
+                id: __newPasswordArea
+                anchors.top: newPasswordArea.bottom
+                anchors.topMargin: 20
+                anchors.left: parent.left
+                anchors.leftMargin: 50
+                width: 697
+                height: 66
+
+                color: "transparent"
+                // border.color: "#40A9FF"
+                radius: 12
+
+                Text {
+                    id: __newPasswordLabel
+                    anchors.top: parent.top
+                    anchors.topMargin: 0
+                    anchors.left: parent.left
+                    anchors.leftMargin: 0
+                    // width: 90
+                    height: 21
+
+                    text: qsTr("确认新密码")
+                    color: "#B3C1D1"
+                    font.pixelSize: 18
+                    font.family: iconFont.name
+                    font.bold: true
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                }
+
+                MInput {
+                    id: __newPasswordInput
+                    anchors.top: __newPasswordLabel.bottom
+                    anchors.topMargin: 5
+                    width: 697
+                    height: 40
+
+                    bgBorderColor: "#C340A9FF"
+                    required: true
+                    requiredMsg: "新密码不一致"
+
+                    placeholderText: "请再次输入新密码"
+                    textColor: "#B3C1D1"
+
+                    passwordField: true
+                }
+            }
+
+            RowLayout {
+                id: __btnsRow
+                height: 70
+                anchors.bottom: parent.bottom
+                anchors.bottomMargin: 50
+                anchors.right: parent.right
+                anchors.rightMargin: 50
+
+                MButton {
+                    id: __btnNegative
+                    text: qsTr("取消")
+                    iconCharacter: "\uf057"
+                    iconColor: "white"
+                    height: 40
+
+                    buttonColor: "#4E596999"
+                    fontSize: 16
+                    iconSize: 16
+                    btnRadius: 8
+
+                    MouseArea {
+                        id: negativeDialog
+                        anchors.fill: parent
+
+                        onClicked: {
+                            cancel();
+                        }
+                    }
+                }
+                MButton {
+                    text: qsTr("确定")
+                    iconCharacter: "\uf058"
+                    iconColor: "white"
+                    height: 40
+
+                    buttonColor: "#C940A9FF"
+                    fontSize: 16
+                    iconSize: 16
+                    btnRadius: 8
+
+                    MouseArea {
+                        id: opsitiveDialog
+                        anchors.fill: parent
+
+                        onClicked: {
+                            oldPasswordInput.slotShowRequiredMsg(true);
+                            newPasswordInput.slotShowRequiredMsg(true);
+                            __newPasswordInput.slotShowRequiredMsg(true);
+                            if (newPasswordInput.text !== "" && newPasswordInput.text === __newPasswordInput.text) {
+                                confirm(oldPasswordInput.text, newPasswordInput.text);
+                            } else {
+                                if (newPasswordInput.text !== "") {
+                                    __errorMsgText.text = qsTr("密码不一致")
+                                    errorPopup.open();
+                                    return;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            Popup {
+                id: errorPopup
+                x: parent.width/2 - width/2
+                y: 30
+                modal: true  // 设置是否为模态窗口
+                // focus: visible  // 获取焦点
+                closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside  // 设置关闭策略
+
+                background: Rectangle {
+                    anchors.fill: parent
+                    color: "transparent"
+                }
+
+                Text {
+                    id: __errorMsgText
+                    anchors.centerIn: parent
+
+                    font.pixelSize: 18
+                    font.bold: true
+                    font.family: iconFont.name
+
+                    color: "red"
+
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                    }
+            }
+        }
+    }
+    MouseArea {
+        id: __dialogMouseArea
+        anchors.fill: parent
+        // 防鼠标事件穿透
+        onClicked: function(mouse) { mouse.accepted=true; }
+        onPressed: function(mouse) { mouse.accepted=true; }
+        onReleased: function(mouse) { mouse.accepted=true; }
+        onWheel: function(mouse) { mouse.accepted=true; }
+        onDoubleClicked: function(mouse) { mouse.accepted=true; }
+    }
+}

+ 4 - 16
src/qml/components/UserInfoCard.qml

@@ -5,8 +5,8 @@ Rectangle {
     width: parent.width
     height: parent.height
 
-    border.color: "#40A9FF"
-    color: "#1A40A9FF"
+    border.color: "#4C40A9FF"
+    color: "#0A192999"
     radius: 20
 
     default property alias content: contentItem.data
@@ -30,18 +30,6 @@ Rectangle {
         iconSize: 28
     }
 
-    Rectangle {
-        id: spliterLine
-        height: 2
-        width: parent.width - 90
-        anchors.left: parent.left
-        anchors.leftMargin: 45
-        anchors.rightMargin: 45
-        anchors.top: __titleLabel.bottom
-
-        color: "#40a9ff"
-    }
-
     Item {
         id: contentItem
 
@@ -49,8 +37,8 @@ Rectangle {
         anchors.left: parent.left
         anchors.leftMargin: 45
         anchors.rightMargin: 45
-        anchors.top: spliterLine.bottom
-        anchors.topMargin: 20
+        anchors.top: __titleLabel.bottom
+        anchors.topMargin: 10
         anchors.bottom: parent.bottom
         anchors.bottomMargin: 35
 

+ 4 - 0
src/usr/config.h

@@ -70,6 +70,10 @@ public:
     QString httpHost = "120.27.232.27:9292";
     QString tenant_id = "1";
 
+    QString userInfoUrl = "/admin-api/system/user/profile/get";                                             // 用户中心
+    QString updateUserInfoUrl = "/admin-api/system/user/profile/update";                                    // 更新用户信息
+    QString updatePasswordUrl = "/admin-api/system/user/profile/update-password";                           // 更新用户密码
+
     QString usernameLogin_url = "/admin-api/system/auth/login";                                             //用户名登陆接口
     QString cardLogin_url = "/admin-api/iscs/job-card/loginByCard";                                          //卡号登陆接口
     QString logout_url = "/logout";                                                                         //登出接口