zhuizhu 10 месяцев назад
Родитель
Сommit
0e4550d851
10 измененных файлов с 427 добавлено и 52 удалено
  1. 4 4
      api/tapi.cpp
  2. 32 1
      api/tloginapi.cpp
  3. 19 1
      api/tloginapi.h
  4. 1 0
      appevent.h
  5. 205 8
      teacherServer_zh_CN.ts
  6. 126 38
      tlogin.cpp
  7. 7 0
      tlogin.h
  8. 11 0
      widgets/captchalabel.cpp
  9. 20 0
      widgets/captchalabel.h
  10. 2 0
      widgets/widgets.pri

+ 4 - 4
api/tapi.cpp

@@ -49,7 +49,7 @@ static QMap<RequestCode, QString> errorMessages
        {RequestCode::RealNameVerificationFailed, Tr::tr("Real Name Verification Failed")},
        {RequestCode::VIPExpirationTimeFailed, Tr::tr("VIP Expiration Time Failed")}};
 
-static QString base_url("http://127.0.0.1:8080");
+static QString base_url("http://exam.stem993.cn");
 
 static const QLatin1String scCode("code");
 static const QLatin1String scMessage("message");
@@ -144,9 +144,9 @@ NetworkAccessManager *NetworkAccessManager::instance()
 NetworkAccessManager::NetworkAccessManager(QObject *parent)
     : QNetworkAccessManager(parent)
 {
-    const QString ip = AppEvent::instance()->configValue("serverIP").toString();
-    const QString port = AppEvent::instance()->configValue("serverPort").toString();
-    base_url = QString("http://%1:%2").arg(ip).arg(port);
+    // const QString ip = AppEvent::instance()->configValue("serverIP").toString();
+    // const QString port = AppEvent::instance()->configValue("serverPort").toString();
+    // base_url = QString("http://%1:%2").arg(ip).arg(port);
 }
 
 QNetworkReply *NetworkAccessManager::createRequest(Operation op,

+ 32 - 1
api/tloginapi.cpp

@@ -25,7 +25,7 @@ Login::Login()
     , url("/api/v1/user/login")
 {}
 
-Login::Login(const QString &_user, const QString &_password)
+Login::Login(const QString &_user, const QString &_password, const QString &_captchaId, const QString &_captcha)
     : QObject()
     , url("/api/v1/user/login")
 {
@@ -33,6 +33,12 @@ Login::Login(const QString &_user, const QString &_password)
     json["name"] = _user;
     json["swID"] = _password;
     json["examineeNumber"] = _password;
+    
+    // 添加验证码参数
+    if (!_captchaId.isEmpty() && !_captcha.isEmpty()) {
+        json["captchaId"] = _captchaId;
+        json["captcha"] = _captcha;
+    }
 
     QJsonDocument jsonData(json);
     postData = jsonData.toJson();
@@ -139,4 +145,29 @@ UserInfo::Data UserInfo::get(bool *b)
     }
     return ret;
 }
+// /sys-api/captcha
+TC::Captcha::Captcha()
+    : QObject()
+    , url("/sys-api/captcha")
+{}
+
+TC::Captcha::Data TC::Captcha::get(bool *b)
+{
+    if (b) {
+        *b = false;
+    }
+    Captcha::Data ret;
+    std::optional<QJsonValue> data = sendRequest(QNetworkAccessManager::GetOperation, url);
+    if (!data.has_value()) {
+        return ret;
+    }
+    const QJsonObject &object = data.value().toObject();
+
+    ret.captchaId = object["captchaId"].toString();
+    ret.imgPath = object["imgPath"].toString();
+    if (b) {
+        *b = true;
+    }
+    return ret;
+}
 } // namespace TC

+ 19 - 1
api/tloginapi.h

@@ -16,7 +16,7 @@ public:
         qint64 refreshAfter;
     };
     Login();
-    Login(const QString &user, const QString &password);
+    Login(const QString &user, const QString &password, const QString &captchaId = "", const QString &captcha = "");
 
     bool post();
 
@@ -69,5 +69,23 @@ public:
 
     QString url;
 };
+
+class Captcha : public QObject
+{
+    Q_OBJECT
+public:
+    struct Data
+    {
+        QString captchaId;
+        QString imgPath;
+    };
+    Captcha();
+
+    Data get(bool *b = nullptr);
+
+private:
+    QString url;
+};
+
 } // namespace TC
 #endif // TLOGINAPI_H

+ 1 - 0
appevent.h

@@ -44,6 +44,7 @@ public:
     void setExam(const QString &examRoom, const QString &examNumber);
     QString examRoom() const;
     QString examNumber() const;
+
     // 配置
     bool setConfigValue(const QString &key, const QVariant &value);
     QVariant configValue(const QString &key);

+ 205 - 8
teacherServer_zh_CN.ts

@@ -43,29 +43,29 @@
         <translation type="unfinished">解锁屏幕</translation>
     </message>
     <message>
-        <location filename="examtestpage.cpp" line="119"/>
+        <location filename="examtestpage.cpp" line="125"/>
         <source>info</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="examtestpage.cpp" line="120"/>
+        <location filename="examtestpage.cpp" line="126"/>
         <source>Please set exam questions first</source>
         <translation type="unfinished">请先设置考试题目</translation>
     </message>
     <message>
-        <location filename="examtestpage.cpp" line="123"/>
-        <location filename="examtestpage.cpp" line="133"/>
+        <location filename="examtestpage.cpp" line="129"/>
+        <location filename="examtestpage.cpp" line="139"/>
         <source>OK</source>
         <translation type="unfinished">确定</translation>
     </message>
     <message>
-        <location filename="examtestpage.cpp" line="124"/>
-        <location filename="examtestpage.cpp" line="134"/>
+        <location filename="examtestpage.cpp" line="130"/>
+        <location filename="examtestpage.cpp" line="140"/>
         <source>Cancel</source>
         <translation type="unfinished">取消</translation>
     </message>
     <message>
-        <location filename="examtestpage.cpp" line="130"/>
+        <location filename="examtestpage.cpp" line="136"/>
         <source>The current exam question is:%1</source>
         <translation type="unfinished">当前考试题目是:%1</translation>
     </message>
@@ -568,6 +568,199 @@
         <translation type="unfinished">学校</translation>
     </message>
 </context>
+<context>
+    <name>TLogin</name>
+    <message>
+        <location filename="tlogin.cpp" line="24"/>
+        <location filename="tlogin.cpp" line="57"/>
+        <location filename="tlogin.cpp" line="62"/>
+        <source>Login</source>
+        <translation type="unfinished">登录</translation>
+    </message>
+    <message>
+        <source>link failure</source>
+        <translation type="obsolete">链接失败</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="30"/>
+        <source>Welcome to the system</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="34"/>
+        <source>Enter your user name</source>
+        <translation type="unfinished">输入用户名</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="39"/>
+        <source>Enter your ExamNo</source>
+        <oldsource>Enter your password or swid</oldsource>
+        <translation type="unfinished">输入考试编号</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="43"/>
+        <source>Enter captcha</source>
+        <translation type="unfinished">输入验证码</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="51"/>
+        <source>Click to refresh captcha</source>
+        <translation type="unfinished">点击刷新验证码</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="58"/>
+        <source>Username:</source>
+        <translation type="unfinished">用户名:</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="60"/>
+        <source>Captcha:</source>
+        <translation type="unfinished">验证码:</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="165"/>
+        <source>Failed to get captcha</source>
+        <translation type="unfinished">验证码获取失败</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="199"/>
+        <source>User name or password cannot be empty.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="206"/>
+        <source>Captcha cannot be empty.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="226"/>
+        <source>User name, password or captcha error, please try again.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>ExamNo:</source>
+        <translation type="obsolete">考试编号:</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="59"/>
+        <source>Password:</source>
+        <translation type="unfinished">密码:</translation>
+    </message>
+    <message>
+        <source>link succeeded</source>
+        <translation type="obsolete">链接成功</translation>
+    </message>
+    <message>
+        <location filename="tlogin.cpp" line="198"/>
+        <location filename="tlogin.cpp" line="205"/>
+        <location filename="tlogin.cpp" line="225"/>
+        <source>Login failed</source>
+        <translation type="unfinished">登录失败</translation>
+    </message>
+    <message>
+        <source>User name or password error, please try again.</source>
+        <translation type="obsolete">用户名或密码错误,请重试</translation>
+    </message>
+</context>
+<context>
+    <name>TeacherServer</name>
+    <message>
+        <location filename="api/tapi.cpp" line="34"/>
+        <source>OK</source>
+        <translation type="unfinished">确定</translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="35"/>
+        <source>General Server Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="36"/>
+        <source>Invalid Request</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="37"/>
+        <source>Token Expired</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="38"/>
+        <source>Database Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="39"/>
+        <source>Username Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="40"/>
+        <source>Invalid Phone Format</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="41"/>
+        <source>Password Requirements Not Met</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="42"/>
+        <source>Phone Already Registered</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="43"/>
+        <source>Incorrect Credentials</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="44"/>
+        <source>Get Phone Code Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="45"/>
+        <source>Phone Code Exists</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="46"/>
+        <source>Phone Code Not Found</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="47"/>
+        <source>Correct Verification Code</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="48"/>
+        <source>Update Login Password Failed</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="49"/>
+        <source>Real Name Verification Failed</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="50"/>
+        <source>VIP Expiration Time Failed</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="86"/>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="api/tapi.cpp" line="109"/>
+        <location filename="api/tapi.cpp" line="116"/>
+        <source>Server Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>TeacherServer::TeacherServer</name>
     <message>
@@ -640,7 +833,6 @@
     </message>
     <message>
         <location filename="tcontroller.cpp" line="718"/>
-        <location filename="tcontroller.cpp" line="796"/>
         <source>Update Answer Time Failed</source>
         <translation type="unfinished"></translation>
     </message>
@@ -654,6 +846,11 @@
         <source>Table data </source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="tcontroller.cpp" line="797"/>
+        <source>Get Table Data Failed</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <location filename="tcontroller.cpp" line="83"/>
         <location filename="tcontroller.cpp" line="402"/>

+ 126 - 38
tlogin.cpp

@@ -14,8 +14,12 @@
 #include "qwidget.h"
 #include "utils/layoutbuilder.h"
 
+#include "widgets/captchalabel.h"
+
 #include "api/tloginapi.h"
 
+
+
 TLogin::TLogin(QWidget *parent)
     : QDialog(parent)
 {
@@ -26,38 +30,89 @@ TLogin::TLogin(QWidget *parent)
     using namespace Layouting;
 
     info = new QLabel;
-    info->setStyleSheet("color: white;");
-    info->setText(tr("link failure"));
+    info->setStyleSheet("color: #333333;");
+    info->setText(tr("Welcome to the system"));
+    
     // 创建登录组件
     usernameEdit = new QLineEdit();
     usernameEdit->setPlaceholderText(tr("Enter your user name"));
+    usernameEdit->setMinimumWidth(200);
+    
     passwordEdit = new QLineEdit();
-    // passwordEdit->setEchoMode(QLineEdit::Password);
+    passwordEdit->setEchoMode(QLineEdit::Password);
     passwordEdit->setPlaceholderText(tr("Enter your ExamNo"));
-    // QRegExp regExp("[a-zA-Z0-9]{6,12}");
-    // QValidator *validator = new QRegExpValidator(regExp, passwordEdit);
-    // passwordEdit->setValidator(validator);
-
+    
+    // 添加验证码输入框
+    captchaEdit = new QLineEdit();
+    captchaEdit->setPlaceholderText(tr("Enter captcha"));
+    captchaEdit->setMaxLength(6);
+    
+    // 验证码图片显示
+    captchaImage = new CaptchaLabel();
+    captchaImage->setFixedSize(120, 40);
+    captchaImage->setScaledContents(true);
+    captchaImage->setCursor(Qt::PointingHandCursor); // 设置鼠标指针为手型,提示可点击
+    captchaImage->setToolTip(tr("Click to refresh captcha")); // 添加提示文本
+    
+    // 连接 clicked 信号到刷新验证码的槽函数
+    connect(captchaImage, &CaptchaLabel::clicked, this, &TLogin::getCaptcha);
+    
+    // 创建表单布局,将captchaEdit和captchaImage放在同一行
     QWidget *w = Group{Column{Row{st, tr("Login"), st},
-                              Form{tr("Username:"), usernameEdit, br, tr("ExamNo:"), passwordEdit},
+                              Form{tr("Username:"), usernameEdit, 
+                                   br, tr("Password:"), passwordEdit,
+                                   br, tr("Captcha:"), Row{captchaEdit, captchaImage}},
                               PushButton{bindTo(&loginPushButton),
                                          text(tr("Login")),
                                          onClicked(std::bind(&TLogin::on_login_clicked, this))}}}
                      .emerge();
 
     QPushButton *close;
-    auto h = Row{"", st, PushButton{bindTo(&close), "test"}};
+    auto h = Row{st, PushButton{bindTo(&close), ""}};
 
     Column{h, st, Row{st, w, st}, st, info, noMargin}.attachTo(this);
 
+    // 设置美观的样式表,不使用背景图片
     QString styleSheet = R"(
     QDialog#Login {
-        background-image: url(:/bg/bg1.png);
+        background-color: #2c3e50;
+        border: 1px solid #34495e;
+        border-radius: 5px;
     }
     QGroupBox {
-        background-color: rgba(255, 255, 255, 128);
+        background-color: #ecf0f1;
+        border-radius: 5px;
+        border: 1px solid #bdc3c7;
+        padding: 10px;
+    }
+    QLineEdit {
+        border: 1px solid #bdc3c7;
+        border-radius: 3px;
+        padding: 5px;
+        background-color: white;
+    }
+    QLineEdit:focus {
+        border: 1px solid #3498db;
+        background-color: #f8f9fa;
+    }
+    QPushButton {
+        background-color: #3498db;
+        color: white;
+        border: none;
+        border-radius: 3px;
+        padding: 8px 16px;
+        font-weight: bold;
+    }
+    QPushButton:hover {
+        background-color: #2980b9;
+    }
+    QPushButton:disabled {
+        background-color: #95a5a6;
+    }
+    QLabel {
+        color: #333333;
     }
-)";
+    )"; 
     setStyleSheet(styleSheet);
 
     QPixmap closePix = style()->standardPixmap(QStyle::SP_TitleBarCloseButton);
@@ -66,34 +121,48 @@ TLogin::TLogin(QWidget *parent)
     close->setStyleSheet(R"__(
     QPushButton {
         border: none;
-        background-color: #ffffff; /* 红色背景 */
-        color: white; /* 文字颜色 */
-        padding: 10px 10px; /* 内边距 */
-        text-align: center; /* 文字居中 */
-        text-decoration: none; /* 去掉下划线 */
-        font-size: 12px; /* 字体大小 */
-        margin: 2px 2px; /* 外边距 */
-        border-radius: 10px; /* 圆角 */
+        background-color: transparent;
+        padding: 5px;
     }
     QPushButton:hover {
-        background-color: #d32f2f; /* 鼠标悬停时背景色 */
+        background-color: #e74c3c;
+        border-radius: 3px;
     }
     )__");
 
-    resize({400, 400});
-    // usernameEdit->setText("test");
-    // passwordEdit->setText("SW1111112");
-    loginPushButton->setDisabled(true);
+    resize({400, 450});
+    loginPushButton->setEnabled(true);
     QObject::connect(close, &QPushButton::clicked, this, &TLogin::close);
-    // QObject::connect(AppEvent::instance(), &AppEvent::serverLinkSignals, this, [this](bool isok) {
-    //     if (isok) {
-    //         info->setText(tr("link succeeded"));
-    //         loginPushButton->setDisabled(false);
-    //     } else {
-    //         info->setText(tr("link failure"));
-    //         loginPushButton->setDisabled(true);
-    //     }
-    // });
+    
+    // 获取初始验证码
+    getCaptcha();
+}
+
+void TLogin::getCaptcha()
+{
+    bool ok = false;
+    TC::Captcha::Data captchaData = TC::Captcha().get(&ok);
+    if (ok) {
+        captchaId = captchaData.captchaId;
+        
+        // 处理Base64图片数据
+        QString imgData = captchaData.imgPath;
+        if (imgData.startsWith("data:image/png;base64,")) {
+            imgData = imgData.mid(22); // 移除前缀
+        }
+        
+        QByteArray imageBytes = QByteArray::fromBase64(imgData.toLatin1());
+        QPixmap pixmap;
+        pixmap.loadFromData(imageBytes);
+        captchaImage->setPixmap(pixmap);
+    } else {
+        info->setText(tr("Failed to get captcha"));
+    }
+}
+
+void TLogin::on_refresh_captcha_clicked()
+{
+    getCaptcha();
 }
 
 void TLogin::mousePressEvent(QMouseEvent *event)
@@ -111,27 +180,46 @@ void TLogin::mouseMoveEvent(QMouseEvent *event)
         event->accept();
     }
 }
+
 void TLogin::on_login_clicked()
 {
     const QString userName = usernameEdit->text();
     const QString password = passwordEdit->text();
+    const QString captcha = captchaEdit->text();
 
     if (userName.isEmpty() || password.isEmpty()) {
         QMessageBox::warning(this,
                              tr("Login failed"),
-                             tr("User name or password error, please try again."));
+                             tr("User name or password cannot be empty."));
+        return;
+    }
+    
+    if (captcha.isEmpty()) {
+        QMessageBox::warning(this,
+                             tr("Login failed"),
+                             tr("Captcha cannot be empty."));
         return;
     }
-    bool ok = TC::Login(userName, password).post();
+    
+    // 这里需要修改Login API,添加验证码参数
+    // 由于我们没有看到Login类的完整实现,这里假设需要修改Login构造函数
+    // 实际实现时需要根据后端API的要求进行调整
+    
+    // 修改Login类构造函数,添加captchaId和captcha参数
+    // 这里只是示例,实际实现可能需要调整
+    bool ok = TC::Login(userName, password, captchaId, captcha).post();
+    
     if (ok && AppEvent::instance()->isLogin()) {
         // 登录成功,发出信号
         emit loginSuccessful();
         close();
     } else {
-        // 登录失败,显示提示框
+        // 登录失败,显示提示框并刷新验证码
         QMessageBox::warning(this,
                              tr("Login failed"),
-                             tr("User name or password error, please try again."));
+                             tr("User name, password or captcha error, please try again."));
+        getCaptcha(); // 刷新验证码
+        captchaEdit->clear(); // 清空验证码输入框
         return;
     }
 }

+ 7 - 0
tlogin.h

@@ -14,6 +14,7 @@ public:
     explicit TLogin(QWidget *parent = nullptr);
 
     void on_login_clicked();
+    void on_refresh_captcha_clicked(); // 刷新验证码
 
 protected:
     void mousePressEvent(QMouseEvent *event);
@@ -27,8 +28,14 @@ private:
     QPushButton *loginPushButton;
     QLineEdit *usernameEdit;
     QLineEdit *passwordEdit;
+    QLineEdit *captchaEdit;
 
+    class CaptchaLabel *captchaImage;
     QLabel *info;
+    
+    QString captchaId; // 存储验证码ID
+    
+    void getCaptcha(); // 获取验证码
 };
 
 #endif // TLOGIN_H

+ 11 - 0
widgets/captchalabel.cpp

@@ -0,0 +1,11 @@
+#include "captchalabel.h"
+
+CaptchaLabel::CaptchaLabel(QWidget *parent, Qt::WindowFlags f)
+    : QLabel(parent, f)
+{}
+
+CaptchaLabel::~CaptchaLabel() {}
+
+void CaptchaLabel::mouseReleaseEvent(QMouseEvent *ev) {
+    emit clicked();
+}

+ 20 - 0
widgets/captchalabel.h

@@ -0,0 +1,20 @@
+#ifndef CAPTCHALABEL_H
+#define CAPTCHALABEL_H
+
+#include <QLabel>
+
+class CaptchaLabel : public QLabel
+{
+    Q_OBJECT
+public:
+    explicit CaptchaLabel(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags());
+    ~CaptchaLabel();
+
+signals:
+    void clicked();
+
+protected:
+    void mouseReleaseEvent(QMouseEvent *ev) override;
+};
+
+#endif // CAPTCHALABEL_H

+ 2 - 0
widgets/widgets.pri

@@ -1,4 +1,5 @@
 HEADERS += \
+    $$PWD/captchalabel.h \
     $$PWD/examsmodel.h \
     $$PWD/gradsmodel.h \
     $$PWD/pagination.h \
@@ -6,6 +7,7 @@ HEADERS += \
     $$PWD/tcdelegate.h
 
 SOURCES += \
+    $$PWD/captchalabel.cpp \
     $$PWD/examsmodel.cpp \
     $$PWD/gradsmodel.cpp \
     $$PWD/pagination.cpp \