소스 검색

实现接口

zhuizhu 10 달 전
부모
커밋
021e512299
9개의 변경된 파일303개의 추가작업 그리고 88개의 파일을 삭제
  1. 93 0
      api/gradesapi.cpp
  2. 45 0
      api/gradesapi.h
  3. 14 13
      api/tloginapi.cpp
  4. 1 0
      api/tloginapi.h
  5. 6 1
      tcontroller.cpp
  6. 2 0
      teacherServer.pro
  7. 80 70
      teacherServer_zh_CN.ts
  8. 57 4
      tlogin.cpp
  9. 5 0
      tlogin.h

+ 93 - 0
api/gradesapi.cpp

@@ -0,0 +1,93 @@
+#include "gradesapi.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <optional>
+
+#include "appevent.h"
+#include "tapi.h"
+
+#include <QDebug>
+
+namespace TC {
+
+extern std::optional<QJsonValue> sendRequest(QNetworkAccessManager::Operation op,
+                                      const QString &url,
+                                      const QByteArray &postData);
+
+GradesUpdate::GradesUpdate()
+    : QObject()
+    , url("/score-api/grades/update")
+{}
+
+GradesUpdate::GradesUpdate(const QString &answerFileName, 
+                         const QString &checkinNumber,
+                         int endTime,
+                         const QString &examRoom,
+                         const QString &examSessions,
+                         const QString &examText,
+                         const QString &examineNumber,
+                         const QString &groupName,
+                         const QString &id,
+                         const QString &name,
+                         int onlineStatus,
+                         const QString &schoolAddress,
+                         const QString &schoolName,
+                         int sex,
+                         int startTime,
+                         int status,
+                         const QString &swId)
+    : QObject()
+    , url("/score-api/grades/update")
+{
+    QJsonObject json;
+    json["answerFileName"] = answerFileName;
+    json["checkinNumber"] = checkinNumber;
+    json["endTime"] = endTime;
+    json["examRoom"] = examRoom;
+    json["examSessions"] = examSessions;
+    json["examText"] = examText;
+    json["examineNumber"] = examineNumber;
+    json["groupName"] = groupName;
+    json["id"] = id;
+    json["name"] = name;
+    json["onlineStatus"] = onlineStatus;
+    json["schoolAddress"] = schoolAddress;
+    json["schoolName"] = schoolName;
+    json["sex"] = sex;
+    json["startTime"] = startTime;
+    json["status"] = status;
+    json["swId"] = swId;
+
+    QJsonDocument jsonData(json);
+    postData = jsonData.toJson();
+}
+
+GradesUpdate::Data GradesUpdate::post()
+{
+    GradesUpdate::Data ret;
+    ret.success = false;
+    ret.message = "Failed to update grades";
+    
+    std::optional<QJsonValue> data = sendRequest(QNetworkAccessManager::PostOperation,
+                                                 url,
+                                                 postData);
+    if (!data.has_value()) {
+        return ret;
+    }
+    
+    const QJsonObject &object = data.value().toObject();
+    qDebug() << object;
+    ret.success = object["status"].toBool();
+    ret.message = object["message"].toString();
+    
+    // 如果成功,触发考试更新事件
+    if (ret.success) {
+        AppEvent::instance()->examsTestUpdate();
+    }
+    
+    return ret;
+}
+
+} // namespace TC

+ 45 - 0
api/gradesapi.h

@@ -0,0 +1,45 @@
+#ifndef GRADESAPI_H
+#define GRADESAPI_H
+
+#include <QObject>
+
+namespace TC {
+
+class GradesUpdate : public QObject
+{
+    Q_OBJECT
+public:
+    struct Data
+    {
+        bool success;
+        QString message;
+    };
+    
+    GradesUpdate();
+    GradesUpdate(const QString &answerFileName, 
+                const QString &checkinNumber,
+                int endTime,
+                const QString &examRoom,
+                const QString &examSessions,
+                const QString &examText,
+                const QString &examineNumber,
+                const QString &groupName,
+                const QString &id,
+                const QString &name,
+                int onlineStatus,
+                const QString &schoolAddress,
+                const QString &schoolName,
+                int sex,
+                int startTime,
+                int status,
+                const QString &swId);
+
+    Data post();
+
+    QString url;
+    QByteArray postData;
+};
+
+} // namespace TC
+
+#endif // GRADESAPI_H

+ 14 - 13
api/tloginapi.cpp

@@ -16,23 +16,22 @@ std::optional<QJsonValue> sendRequest(QNetworkAccessManager::Operation op,
                                       const QByteArray &postData = QByteArray());
 
 static const QLatin1String scJwtToken("JwtToken");
-static const QLatin1String scAccessToken("access_token");
+static const QLatin1String scAccessToken("token");
 static const QLatin1String scAccessExpire("access_expire");
 static const QLatin1String scRefreshAfter("refresh_after");
 
 Login::Login()
     : QObject()
-    , url("/api/v1/user/login")
+    , url("/sys-api/user/login")
 {}
 
 Login::Login(const QString &_user, const QString &_password, const QString &_captchaId, const QString &_captcha)
     : QObject()
-    , url("/api/v1/user/login")
+    , url("/sys-api/user/login")
 {
     QJsonObject json;
-    json["name"] = _user;
-    json["swID"] = _password;
-    json["examineeNumber"] = _password;
+    json["username"] = _user;
+    json["password"] = _password;
     
     // 添加验证码参数
     if (!_captchaId.isEmpty() && !_captcha.isEmpty()) {
@@ -55,15 +54,17 @@ bool Login::post()
     const QJsonObject &object = data.value().toObject();
 
     Login::Data ret;
-
-    if (object[scJwtToken].isObject()) {
-        const QJsonObject &jwtObject = object[scJwtToken].toObject();
-        ret.accessToken = jwtObject[scAccessToken].toString();
-        ret.accessExpire = jwtObject[scAccessExpire].toVariant().toLongLong();
-        ret.refreshAfter = jwtObject[scRefreshAfter].toVariant().toLongLong();
-    }
+    ret.accessToken = object["token"].toString();
+    ret.accessExpire = object["expire"].toVariant().toLongLong();
+    ret.userID = object["userId"].toString();
+    
     qDebug() << object;
 
+    // 计算refreshAfter:当前时间 + (过期时间 - 当前时间) * 2/3
+    qint64 currentTime = AppEvent::time();
+    qint64 timeToExpire = ret.accessExpire - currentTime;
+    ret.refreshAfter = currentTime + (timeToExpire * 2 / 3);
+
     //设置 token 到全局变量
     if (ret.accessExpire > AppEvent::time()) {
         AppEvent::instance()->setJwtToken(ret.accessToken);

+ 1 - 0
api/tloginapi.h

@@ -14,6 +14,7 @@ public:
         QString accessToken;
         qint64 accessExpire;
         qint64 refreshAfter;
+        QString userID;
     };
     Login();
     Login(const QString &user, const QString &password, const QString &captchaId = "", const QString &captcha = "");

+ 6 - 1
tcontroller.cpp

@@ -69,7 +69,12 @@ static QStringList listFiles(const QString &path, const QString &basePath)
 void ConfigController::doGet(CWF::Request &request, CWF::Response &response) const
 {
     // IP 地址是自己配置
-    static QStringList filter = {"serverIP", "serverPort", "webSocketIP", "webSocketPort"};
+    static QStringList filter = {"serverIP",
+                                 "serverPort",
+                                 "webSocketIP",
+                                 "webSocketPort",
+                                 "savedUsername",
+                                 "savedPassword"};
 
     QJsonObject responseConfig;
     const QJsonObject object = AppEvent::instance()->config();

+ 2 - 0
teacherServer.pro

@@ -9,6 +9,7 @@ CONFIG += c++17
 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
 
 SOURCES += \
+    api/gradesapi.cpp \
     api/tapi.cpp \
     api/tloginapi.cpp \
     appevent.cpp \
@@ -25,6 +26,7 @@ SOURCES += \
     websocketserver.cpp
 
 HEADERS += \
+    api/gradesapi.h \
     api/tapi.h \
     api/tloginapi.h \
     appevent.h \

+ 80 - 70
teacherServer_zh_CN.ts

@@ -194,7 +194,7 @@
     <message>
         <location filename="gradespage.cpp" line="126"/>
         <source>Clear...</source>
-        <translation type="unfinished">清空</translation>
+        <translation>清空...</translation>
     </message>
     <message>
         <location filename="gradespage.cpp" line="127"/>
@@ -462,7 +462,7 @@
     <message>
         <location filename="studentpage.cpp" line="136"/>
         <source>Clear...</source>
-        <translation type="unfinished">清空</translation>
+        <translation>清空...</translation>
     </message>
     <message>
         <location filename="studentpage.cpp" line="137"/>
@@ -571,78 +571,88 @@
 <context>
     <name>TLogin</name>
     <message>
-        <location filename="tlogin.cpp" line="24"/>
-        <location filename="tlogin.cpp" line="57"/>
-        <location filename="tlogin.cpp" line="62"/>
+        <location filename="tlogin.cpp" line="29"/>
+        <location filename="tlogin.cpp" line="65"/>
+        <location filename="tlogin.cpp" line="71"/>
         <source>Login</source>
-        <translation type="unfinished">登录</translation>
+        <translation>登录</translation>
     </message>
     <message>
         <source>link failure</source>
         <translation type="obsolete">链接失败</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="30"/>
+        <location filename="tlogin.cpp" line="35"/>
         <source>Welcome to the system</source>
-        <translation type="unfinished"></translation>
+        <translation>欢迎使用系统</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="34"/>
+        <location filename="tlogin.cpp" line="39"/>
         <source>Enter your user name</source>
-        <translation type="unfinished">输入用户名</translation>
+        <translation>输入用户名</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="39"/>
+        <location filename="tlogin.cpp" line="44"/>
         <source>Enter your ExamNo</source>
         <oldsource>Enter your password or swid</oldsource>
-        <translation type="unfinished">输入考试编号</translation>
+        <translation>输入密码</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="43"/>
+        <location filename="tlogin.cpp" line="48"/>
         <source>Enter captcha</source>
-        <translation type="unfinished">输入验证码</translation>
+        <translation>输入验证码</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="51"/>
+        <location filename="tlogin.cpp" line="56"/>
         <source>Click to refresh captcha</source>
-        <translation type="unfinished">点击刷新验证码</translation>
+        <translation>点击刷新验证码</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="59"/>
+        <source>Remember Username</source>
+        <translation>记住用户名</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="58"/>
+        <location filename="tlogin.cpp" line="66"/>
         <source>Username:</source>
-        <translation type="unfinished">用户名:</translation>
+        <translation>用户名:</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="60"/>
+        <location filename="tlogin.cpp" line="68"/>
         <source>Captcha:</source>
-        <translation type="unfinished">验证码:</translation>
+        <translation>验证码:</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="165"/>
+        <location filename="tlogin.cpp" line="74"/>
+        <source>Offline Login</source>
+        <translation>离线登录</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="187"/>
         <source>Failed to get captcha</source>
-        <translation type="unfinished">验证码获取失败</translation>
+        <translation>验证码获取失败</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="199"/>
+        <location filename="tlogin.cpp" line="221"/>
         <source>User name or password cannot be empty.</source>
-        <translation type="unfinished"></translation>
+        <translation>用户名或密码不能为空.</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="206"/>
+        <location filename="tlogin.cpp" line="228"/>
         <source>Captcha cannot be empty.</source>
-        <translation type="unfinished"></translation>
+        <translation>验证码不能为空.</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="226"/>
+        <location filename="tlogin.cpp" line="260"/>
         <source>User name, password or captcha error, please try again.</source>
-        <translation type="unfinished"></translation>
+        <translation>用户名或密码或验证码错误, 请重新尝试登录.</translation>
     </message>
     <message>
         <source>ExamNo:</source>
         <translation type="obsolete">考试编号:</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="59"/>
+        <location filename="tlogin.cpp" line="67"/>
         <source>Password:</source>
         <translation type="unfinished">密码:</translation>
     </message>
@@ -651,9 +661,9 @@
         <translation type="obsolete">链接成功</translation>
     </message>
     <message>
-        <location filename="tlogin.cpp" line="198"/>
-        <location filename="tlogin.cpp" line="205"/>
-        <location filename="tlogin.cpp" line="225"/>
+        <location filename="tlogin.cpp" line="220"/>
+        <location filename="tlogin.cpp" line="227"/>
+        <location filename="tlogin.cpp" line="259"/>
         <source>Login failed</source>
         <translation type="unfinished">登录失败</translation>
     </message>
@@ -764,104 +774,104 @@
 <context>
     <name>TeacherServer::TeacherServer</name>
     <message>
-        <location filename="tcontroller.cpp" line="126"/>
-        <location filename="tcontroller.cpp" line="228"/>
-        <location filename="tcontroller.cpp" line="297"/>
-        <location filename="tcontroller.cpp" line="547"/>
-        <location filename="tcontroller.cpp" line="649"/>
+        <location filename="tcontroller.cpp" line="131"/>
+        <location filename="tcontroller.cpp" line="233"/>
+        <location filename="tcontroller.cpp" line="302"/>
+        <location filename="tcontroller.cpp" line="552"/>
+        <location filename="tcontroller.cpp" line="654"/>
         <source>user does not exist</source>
         <translation type="unfinished">用户不存在</translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="112"/>
-        <location filename="tcontroller.cpp" line="159"/>
+        <location filename="tcontroller.cpp" line="117"/>
+        <location filename="tcontroller.cpp" line="164"/>
         <source>Login succeeded</source>
         <translation type="unfinished">登录成功</translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="176"/>
-        <location filename="tcontroller.cpp" line="252"/>
-        <location filename="tcontroller.cpp" line="321"/>
+        <location filename="tcontroller.cpp" line="181"/>
+        <location filename="tcontroller.cpp" line="257"/>
+        <location filename="tcontroller.cpp" line="326"/>
         <source>Login failed</source>
         <translation type="unfinished">登录失败</translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="187"/>
-        <location filename="tcontroller.cpp" line="262"/>
-        <location filename="tcontroller.cpp" line="513"/>
+        <location filename="tcontroller.cpp" line="192"/>
+        <location filename="tcontroller.cpp" line="267"/>
+        <location filename="tcontroller.cpp" line="518"/>
         <source>LoginOut failed, no find user</source>
         <translation type="unfinished">退出失败, 没有发现用户</translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="196"/>
-        <location filename="tcontroller.cpp" line="271"/>
-        <location filename="tcontroller.cpp" line="342"/>
-        <location filename="tcontroller.cpp" line="424"/>
-        <location filename="tcontroller.cpp" line="522"/>
-        <location filename="tcontroller.cpp" line="613"/>
+        <location filename="tcontroller.cpp" line="201"/>
+        <location filename="tcontroller.cpp" line="276"/>
+        <location filename="tcontroller.cpp" line="347"/>
+        <location filename="tcontroller.cpp" line="429"/>
+        <location filename="tcontroller.cpp" line="527"/>
+        <location filename="tcontroller.cpp" line="618"/>
         <source>unauthorized</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="390"/>
-        <location filename="tcontroller.cpp" line="409"/>
-        <location filename="tcontroller.cpp" line="473"/>
+        <location filename="tcontroller.cpp" line="395"/>
+        <location filename="tcontroller.cpp" line="414"/>
+        <location filename="tcontroller.cpp" line="478"/>
         <source>Failed to obtain exam content</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="533"/>
-        <location filename="tcontroller.cpp" line="587"/>
+        <location filename="tcontroller.cpp" line="538"/>
+        <location filename="tcontroller.cpp" line="592"/>
         <source>file uploaded successfully</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="593"/>
+        <location filename="tcontroller.cpp" line="598"/>
         <source>Failed to uploaded files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="604"/>
+        <location filename="tcontroller.cpp" line="609"/>
         <source>answer Time failed, no find user login</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="625"/>
-        <location filename="tcontroller.cpp" line="711"/>
+        <location filename="tcontroller.cpp" line="630"/>
+        <location filename="tcontroller.cpp" line="716"/>
         <source>Update Answer Time successfully</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="718"/>
+        <location filename="tcontroller.cpp" line="723"/>
         <source>Update Answer Time Failed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="729"/>
+        <location filename="tcontroller.cpp" line="734"/>
         <source>json param error</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="790"/>
+        <location filename="tcontroller.cpp" line="795"/>
         <source>Table data </source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="797"/>
+        <location filename="tcontroller.cpp" line="802"/>
         <source>Get Table Data Failed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="83"/>
-        <location filename="tcontroller.cpp" line="402"/>
+        <location filename="tcontroller.cpp" line="88"/>
+        <location filename="tcontroller.cpp" line="407"/>
         <source>ok</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="tcontroller.cpp" line="213"/>
-        <location filename="tcontroller.cpp" line="244"/>
-        <location filename="tcontroller.cpp" line="282"/>
-        <location filename="tcontroller.cpp" line="313"/>
+        <location filename="tcontroller.cpp" line="218"/>
+        <location filename="tcontroller.cpp" line="249"/>
+        <location filename="tcontroller.cpp" line="287"/>
+        <location filename="tcontroller.cpp" line="318"/>
         <source>LoginOut succeeded</source>
         <translation type="unfinished">退出成功</translation>
     </message>

+ 57 - 4
tlogin.cpp

@@ -9,7 +9,8 @@
 #include <QMouseEvent>
 #include <QRegExpValidator>
 
-#include "qpushbutton.h"
+#include <QCheckBox>
+#include <QPushButton>
 #include "qstyle.h"
 #include "qwidget.h"
 #include "utils/layoutbuilder.h"
@@ -54,6 +55,9 @@ TLogin::TLogin(QWidget *parent)
     captchaImage->setCursor(Qt::PointingHandCursor); // 设置鼠标指针为手型,提示可点击
     captchaImage->setToolTip(tr("Click to refresh captcha")); // 添加提示文本
     
+    // 添加记住用户名复选框
+    rememberUsernameCheckBox = new QCheckBox(tr("Remember Username"));
+    
     // 连接 clicked 信号到刷新验证码的槽函数
     connect(captchaImage, &CaptchaLabel::clicked, this, &TLogin::getCaptcha);
     
@@ -61,10 +65,15 @@ TLogin::TLogin(QWidget *parent)
     QWidget *w = Group{Column{Row{st, tr("Login"), st},
                               Form{tr("Username:"), usernameEdit, 
                                    br, tr("Password:"), passwordEdit,
-                                   br, tr("Captcha:"), Row{captchaEdit, captchaImage}},
-                              PushButton{bindTo(&loginPushButton),
+                                   br, tr("Captcha:"), Row{captchaEdit, captchaImage},
+                                   br, "", rememberUsernameCheckBox},
+                              Row{PushButton{bindTo(&loginPushButton),
                                          text(tr("Login")),
-                                         onClicked(std::bind(&TLogin::on_login_clicked, this))}}}
+                                         onClicked(std::bind(&TLogin::on_login_clicked, this))},
+                                  PushButton{bindTo(&offlineLoginButton),
+                                         text(tr("Offline Login")),
+                                         onClicked(std::bind(&TLogin::on_offline_login_clicked, this))}}
+                             }}
                      .emerge();
 
     QPushButton *close;
@@ -134,10 +143,29 @@ TLogin::TLogin(QWidget *parent)
     loginPushButton->setEnabled(true);
     QObject::connect(close, &QPushButton::clicked, this, &TLogin::close);
     
+    // 加载保存的用户名
+    loadSavedUsername();
+    
     // 获取初始验证码
     getCaptcha();
 }
 
+// 加载保存的用户名
+void TLogin::loadSavedUsername()
+{
+    // 从配置中读取保存的用户名
+    QVariant savedUsername = AppEvent::instance()->configValue("savedUsername");
+    if (!savedUsername.isNull() && savedUsername.isValid()) {
+        usernameEdit->setText(savedUsername.toString());
+        rememberUsernameCheckBox->setChecked(true);
+    }
+    QVariant savedPassword = AppEvent::instance()->configValue("savedPassword");
+    if (!savedPassword.isNull() && savedPassword.isValid()) {
+        passwordEdit->setText(savedPassword.toString());
+        rememberUsernameCheckBox->setChecked(true);
+    }
+}
+
 void TLogin::getCaptcha()
 {
     bool ok = false;
@@ -210,6 +238,19 @@ void TLogin::on_login_clicked()
     bool ok = TC::Login(userName, password, captchaId, captcha).post();
     
     if (ok && AppEvent::instance()->isLogin()) {
+        // 如果选择了记住用户名,则保存用户名到配置
+        qDebug() << rememberUsernameCheckBox->isChecked();
+        if (rememberUsernameCheckBox->isChecked()) {
+            AppEvent::instance()->setConfigValue("savedUsername", userName);
+            AppEvent::instance()->setConfigValue("savedPassword", password);
+            AppEvent::instance()->configSave(); // 保存配置到文件
+        } else {
+            // 如果未选择记住用户名,则清除之前保存的用户名
+            AppEvent::instance()->setConfigValue("savedUsername", "");
+            AppEvent::instance()->setConfigValue("savedPassword", "");
+            AppEvent::instance()->configSave(); // 保存配置到文件
+        }
+
         // 登录成功,发出信号
         emit loginSuccessful();
         close();
@@ -223,3 +264,15 @@ void TLogin::on_login_clicked()
         return;
     }
 }
+
+void TLogin::on_offline_login_clicked()
+{
+    // 离线登录不需要验证用户名、密码和验证码
+    // 直接设置一个默认用户名,表示是离线模式
+    AppEvent::instance()->setConfigValue("offlineMode", true);
+    AppEvent::instance()->configSave(); // 保存配置到文件
+    
+    // 发出登录成功信号
+    emit loginSuccessful();
+    close();
+}

+ 5 - 0
tlogin.h

@@ -6,6 +6,7 @@
 
 class QLineEdit;
 class QLabel;
+class QCheckBox;
 
 class TLogin : public QDialog
 {
@@ -14,6 +15,7 @@ public:
     explicit TLogin(QWidget *parent = nullptr);
 
     void on_login_clicked();
+    void on_offline_login_clicked(); // 添加离线登录槽函数
     void on_refresh_captcha_clicked(); // 刷新验证码
 
 protected:
@@ -26,9 +28,11 @@ private:
     QPoint m_dragPosition;
 
     QPushButton *loginPushButton;
+    QPushButton *offlineLoginButton; // 添加离线登录按钮
     QLineEdit *usernameEdit;
     QLineEdit *passwordEdit;
     QLineEdit *captchaEdit;
+    QCheckBox *rememberUsernameCheckBox; // 添加记住用户名复选框
 
     class CaptchaLabel *captchaImage;
     QLabel *info;
@@ -36,6 +40,7 @@ private:
     QString captchaId; // 存储验证码ID
     
     void getCaptcha(); // 获取验证码
+    void loadSavedUsername(); // 加载保存的用户名
 };
 
 #endif // TLOGIN_H