diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e257658..0000000 --- a/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# ---> C++ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - diff --git a/README.md b/README.md deleted file mode 100644 index dc45f65..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# teamwork-project - diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index 9338fb5..0000000 --- a/doc/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# teamwork-project - -## 项目简介 -这是一个面向小学、初中和高中学生的数学学习软件,采用图形化界面操作,提供数学题目生成、在线考试、成绩记录等功能。项目基于C++和Qt框架开发,实现了完整的用户注册、登录、考试和成绩管理流程。 - -## 项目特点 -1.多学段支持:覆盖小学、初中、高中三个学段的数学题目 -2.安全认证:支持用户注册、登录、密码修改功能 -3.智能出题:根据学段自动生成相应难度的数学题目 -4.实时评分:自动批改试卷并显示详细成绩 -5.数据持久化:本地文件存储用户数据和考试记录 -6.邮箱验证:支持真实邮箱验证码发送功能 - -## 功能模块 - -## 用户管理 -1.用户注册:邮箱验证、密码强度校验 -2.用户登录:安全的身份认证 -3.密码修改:支持在线修改密码 - -## 考试系统 -1.题目生成:根据学段生成不同难度的选择题 -2.考试界面:清晰的题目展示和选项选择 -3.进度跟踪:实时显示答题进度 -4.成绩统计:自动计算得分和正确率 - -## 数据管理 -1.用户数据:本地文件存储用户信息 -2.考试记录:保存历史考试成绩 -3.题目文件:导出生成的题目试卷 - -## 技术架构 - -## 前端界面 -Qt Widgets:跨平台的图形界面框架 -响应式设计:自适应不同屏幕尺寸 -美观UI:现代化的界面风格设计 - -## 后端逻辑 -C++11/14:核心业务逻辑实现 -面向对象设计:模块化的类结构 -跨平台支持:Windows/Linux/macOS兼容 - -## 核心类说明 -## 前端类 -MainWindow:主窗口控制器 -LoginWidget:登录界面 -RegisterWidget:注册界面 -MainMenuWidget:主菜单界面 -ExamWidget:考试界面 -ResultWidget:成绩界面 - -## 后端类 -User:用户信息管理 -UserManager:用户认证和注册 -QuestionGenerator:题目生成器 -FileSaver:文件存储管理 - -## 项目结构 -teamwork-project/ -├── src/ # 源代码目录 -│ ├── frontend/ # 前端代码目录 -│ │ ├── mathlearningApp.pro # Qt项目配置文件 -│ │ ├── main.cpp # 程序入口 -│ │ ├── mainwindow.h/cpp # 主窗口控制器 -│ │ ├── loginwidget.h/cpp # 登录界面 -│ │ ├── registerwidget.h/cpp # 注册界面 -│ │ ├── mainmenuwidget.h/cpp # 主菜单界面 -│ │ ├── examwidget.h/cpp # 考试界面 -│ │ └── resultwidget.h/cpp # 成绩界面 -│ └── backend/ # 后端代码目录 -│ ├── user.h/cpp # 用户类 -│ ├── usermanage.h/cpp # 用户管理 -│ ├── questiongenerator.h/cpp # 题目生成器 -│ └── filesaver.h/cpp # 文件存储 -└── doc/ # 文档目录 - └── README.md # 项目说明 - -## 编译运行 -双击.exe文件运行 - -## 环境要求 -Qt 5.12 或更高版本 -C++11 兼容编译器 -CMake 3.10 或更高版本 - -## 使用说明 -## 首次使用 -运行程序,进入登录界面 -点击"注册新账号"创建用户账户 -输入邮箱获取验证码完成注册 -设置符合要求的密码(6-10位,包含大小写字母和数字) - -## 参加考试 -登录成功后进入主菜单 -选择考试年级和题目数量(5-30题) -点击"开始考试"进入考试界面 -逐题作答,可前后翻页检查 -提交后查看成绩和正确率 - -## 架构设计说明 -## 前后端分离 -前端:负责UI展示、用户交互、界面逻辑 -后端:负责业务逻辑、数据处理、算法实现 -通信方式:通过类接口和信号槽机制进行通信 - -## 设计模式 -MVC模式:清晰的数据-视图-控制器分离 -观察者模式:通过Qt信号槽实现组件间通信 -单例模式:用户管理等核心服务 - -## 问题反馈 -如在使用过程中遇到问题,请联系开发团队或提交Issue。 \ No newline at end of file diff --git a/src/back/filesaver.cpp b/src/back/filesaver.cpp new file mode 100644 index 0000000..d9d724d --- /dev/null +++ b/src/back/filesaver.cpp @@ -0,0 +1,205 @@ +#include "filesaver.h" +#include +#include +#include +#include +#include + +//创建目录(跨平台实现) +bool FileSaver::createDirectory(const std::wstring& wpath) { +#ifdef _WIN32 + return _wmkdir(wpath.c_str()) == 0; +#else + std::string path; + for (wchar_t wc : wpath) { + if (wc < 128) path += static_cast(wc); + } + return mkdir(path.c_str(), 0755) == 0; +#endif +} + +//将包含中文的用户名转换为安全的英文文件名 +std::string FileSaver::usernameToSafeName(const std::wstring& username) { + std::string result; + for (wchar_t wc : username) { + if ((wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || + (wc >= L'a' && wc <= L'z') || wc == L'_') { + result += static_cast(wc); + } else { + switch(wc) { + case L'张': result += "Zhang"; break; + case L'三': result += "San"; break; + case L'李': result += "Li"; break; + case L'四': result += "Si"; break; + case L'王': result += "Wang"; break; + case L'五': result += "Wu"; break; + default: result += "User"; break; + } + } + } + return result; +} + +//将中文年级转换为英文标识 +std::string FileSaver::gradeToEnglish(const std::wstring& grade) { + if (grade == L"小学") return "Primary"; + if (grade == L"初中") return "Junior"; + if (grade == L"高中") return "Senior"; + return "Unknown"; +} + +//宽字符串到普通字符串的转换 +std::string FileSaver::wstringToString(const std::wstring& wstr) { + std::string result; + for (wchar_t wc : wstr) { + if (wc < 128) { + result += static_cast(wc); + } + } + return result; +} + +//将题目列表写入文件的实现 +bool FileSaver::writeQuestionsToFile(const std::string& filename, + const std::string& safeUsername, + const std::string& englishGrade, + const std::vector& questions) { + std::ofstream file(filename); + if (!file.is_open()) { + return false; + } + + file << "Math Exam Paper\n"; + file << "Username: " << safeUsername << "\n"; + file << "Grade: " << englishGrade << "\n"; + file << "Time: " << wstringToString(getCurrentTime()) << "\n\n"; + + for (size_t i = 0; i < questions.size(); i++) { + file << (i + 1) << ". "; + + for (wchar_t wc : questions[i]) { + switch(wc) { + case L'√': file << "√"; break; + case L'²': file << "²"; break; + case L's': file << "s"; break; + case L'i': file << "i"; break; + case L'n': file << "n"; break; + case L'c': file << "c"; break; + case L'o': file << "o"; break; + case L't': file << "t"; break; + case L'a': file << "a"; break; + case L'(': file << "("; break; + case L')': file << ")"; break; + case L'+': file << "+"; break; + case L'-': file << "-"; break; + case L'×': file << "×"; break; + case L'÷': file << "÷"; break; + case L'=': file << "="; break; + case L'?': file << "?"; break; + case L' ': file << " "; break; + case L'0': case L'1': case L'2': case L'3': case L'4': + case L'5': case L'6': case L'7': case L'8': case L'9': + file << static_cast(wc); + break; + default: file << static_cast(wc); break; + } + } + file << "\n\n"; + } + + file.close(); + return true; +} + +//准备文件和目录的实现 +std::string FileSaver::prepareFileAndDirectory(const std::wstring& username, + const std::wstring& grade) { + std::string safeUsername = usernameToSafeName(username); + std::string englishGrade = gradeToEnglish(grade); + std::string folderPath = "exam_papers/" + safeUsername + "_" + englishGrade + "_Papers"; + + // 创建目录 + std::wstring wfolderPath; + for (char c : folderPath) { + wfolderPath += wchar_t(c); + } + createDirectory(wfolderPath); + + return folderPath + "/" + wstringToString(generateFilename()) + ".txt"; +} + +//保存题目的完整流程实现 +bool FileSaver::saveToFile(const std::wstring& username, + const std::wstring& grade, + const std::vector& questions) { + std::string safeUsername = usernameToSafeName(username); + std::string englishGrade = gradeToEnglish(grade); + std::string filename = prepareFileAndDirectory(username, grade); + + bool success = writeQuestionsToFile(filename, safeUsername, englishGrade, questions); + + return success; +} + +//生成基于时间戳的文件名实现 +std::wstring FileSaver::generateFilename() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::tm tm = *std::localtime(&time_t); + std::wostringstream oss; + + oss << tm.tm_year + 1900 << L"_" + << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"_" + << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L"_" + << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L"_" + << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L"_" + << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; + + return oss.str(); +} + +//获取当前格式化时间的实现 +std::wstring FileSaver::getCurrentTime() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::tm tm = *std::localtime(&time_t); + std::wostringstream oss; + + oss << tm.tm_year + 1900 << L"-" + << (tm.tm_mon + 1 < 10 ? L"0" : L"") << tm.tm_mon + 1 << L"-" + << (tm.tm_mday < 10 ? L"0" : L"") << tm.tm_mday << L" " + << (tm.tm_hour < 10 ? L"0" : L"") << tm.tm_hour << L":" + << (tm.tm_min < 10 ? L"0" : L"") << tm.tm_min << L":" + << (tm.tm_sec < 10 ? L"0" : L"") << tm.tm_sec; + + return oss.str(); +} + +//保存考试记录的实现 +bool FileSaver::saveExamRecord(const std::wstring& username, + const std::wstring& grade, + int score, + int totalQuestions, + const std::wstring& examDate) { + std::string safeUsername = usernameToSafeName(username); + std::string filename = "exam_records/" + safeUsername + "_records.txt"; + + // 创建目录 + createDirectory(L"exam_records"); + + std::ofstream file(filename, std::ios::app); + if (!file.is_open()) { + return false; + } + + file << "Date: " << wstringToString(examDate) << "\n"; + file << "Grade: " << gradeToEnglish(grade) << "\n"; + file << "Score: " << score << "/" << totalQuestions << "\n"; + file << "Percentage: " << (score * 100.0 / totalQuestions) << "%\n"; + file << "------------------------\n"; + + file.close(); + return true; +} diff --git a/src/back/filesaver.h b/src/back/filesaver.h new file mode 100644 index 0000000..4f56cc2 --- /dev/null +++ b/src/back/filesaver.h @@ -0,0 +1,63 @@ +#ifndef FILESAVER_H +#define FILESAVER_H + +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#else + #include + #include +#endif + +// 文件保存器类,负责将生成的数学题目保存到文件系统 +class FileSaver { +public: + //将题目列表写入指定文件 + static bool writeQuestionsToFile(const std::string& filename, + const std::string& safeUsername, + const std::string& englishGrade, + const std::vector& questions); + + //准备用户专属文件夹和文件路径 + static std::string prepareFileAndDirectory(const std::wstring& username, + const std::wstring& grade); + + //保存题目列表到用户专属文件 + static bool saveToFile(const std::wstring& username, + const std::wstring& grade, + const std::vector& questions); + + //生成基于当前时间戳的文件名 + static std::wstring generateFilename(); + + //获取当前格式化的时间字符串 + static std::wstring getCurrentTime(); + + //保存考试记录 + static bool saveExamRecord(const std::wstring& username, + const std::wstring& grade, + int score, + int totalQuestions, + const std::wstring& examDate); + +private: + //将包含中文的用户名转换为安全的英文文件名 + static std::string usernameToSafeName(const std::wstring& username); + + //将中文年级转换为英文标识 + static std::string gradeToEnglish(const std::wstring& grade); + + //宽字符串到普通字符串的转换 + static std::string wstringToString(const std::wstring& wstr); + + //创建目录(跨平台实现) + static bool createDirectory(const std::wstring& wpath); +}; + +#endif \ No newline at end of file diff --git a/src/back/questiongenerator.cpp b/src/back/questiongenerator.cpp new file mode 100644 index 0000000..d19e285 --- /dev/null +++ b/src/back/questiongenerator.cpp @@ -0,0 +1,596 @@ +#include "questiongenerator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//题目生成器构造函数的实现 +QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) {} + +//设置年级的实现 +void QuestionGenerator::setGrade(Grade grade) { + currentGrade = grade; + qDebug() << "QuestionGenerator: 设置年级为:" << static_cast(grade); +} + +//生成随机数的实现 +std::wstring QuestionGenerator::generateNumber(int min, int max) { + std::uniform_int_distribution<> dis(min, max); + return std::to_wstring(dis(gen)); +} + +//生成随机运算符的实现 +std::wstring QuestionGenerator::generateOperator() { + std::vector operators = {L"+", L"-", L"×", L"÷"}; + std::uniform_int_distribution<> dis(0, static_cast(operators.size()) - 1); + return operators[dis(gen)]; +} + +//括号需求判断的辅助函数 +bool QuestionGenerator::needsParentheses(const std::wstring& expr, const std::wstring& nextOp) { + if (expr.find(L"+") != std::wstring::npos || expr.find(L"-") != std::wstring::npos) { + if (nextOp == L"×" || nextOp == L"÷") { + return true; + } + } + return false; +} + +//生成小学题目的实现 - 至少2个操作数 +std::wstring QuestionGenerator::generatePrimaryQuestion() { + std::uniform_int_distribution<> operandDis(2, 5); // 2-5个操作数(至少2个) + int operandCount = operandDis(gen); + std::wstring question; + std::vector numbers; + std::vector operators; + + // 生成操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 简单的括号逻辑:在需要时添加括号 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + + return question + L" = ?"; +} + +//生成初中题目的实现 - 可以为1个操作数 +std::wstring QuestionGenerator::generateJuniorQuestion() { + std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) + int operandCount = operandDis(gen); + std::uniform_int_distribution<> typeDis(0, 1); // 0:平方, 1:开根号 + + std::wstring question; + + // 如果只有一个操作数,直接生成平方或开根号题目 + if (operandCount == 1) { + int specialType = typeDis(gen); + if (specialType == 0) { + // 平方 + std::wstring number = generateNumber(1, 100); + question = number + L"² = ?"; + } else { + // 开根号 - 确保是平方数 + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" = ?"; + } + return question; // 直接返回,不再添加 = ? + } else { + // 多个操作数的情况 + std::vector numbers; + std::vector operators; + + // 生成基础操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 随机选择一个位置插入平方或开根号 + int specialPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); + int specialType = typeDis(gen); + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 在指定位置插入特殊运算符 + if (static_cast(i) == specialPos) { + if (specialType == 0) { + // 平方 + question = L"(" + question + L")²"; + } else { + // 开根号 - 确保是平方数 + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" " + operators[i] + L" " + question; + } + } else { + // 简单的括号逻辑 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + } + + // 如果特殊运算符在最后一个位置 + if (specialPos == static_cast(operators.size())) { + if (specialType == 0) { + question = L"(" + question + L")²"; + } else { + int num = std::uniform_int_distribution<>(1, 20)(gen); + int square = num * num; + question = L"√" + std::to_wstring(square) + L" " + operators.back() + L" " + question; + } + } + + return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? + } +} + +//生成高中题目的实现 - 可以为1个操作数 +std::wstring QuestionGenerator::generateSeniorQuestion() { + std::uniform_int_distribution<> operandDis(1, 5); // 1-5个操作数(可以为1) + int operandCount = operandDis(gen); + std::vector trigFunctions = {L"sin", L"cos", L"tan"}; + + std::wstring question; + + // 如果只有一个操作数,直接生成三角函数题目 + if (operandCount == 1) { + int trigType = std::uniform_int_distribution<>(0, 2)(gen); + int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 + question = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°) = ?"; + return question; // 直接返回,不再添加 = ? + } else { + // 多个操作数的情况 + std::vector numbers; + std::vector operators; + + // 生成基础操作数和运算符 + for (int i = 0; i < operandCount; i++) { + numbers.push_back(generateNumber(1, 100)); // 1-100 + if (i < operandCount - 1) { + operators.push_back(generateOperator()); + } + } + + // 随机选择一个位置插入三角函数 + int trigPos = std::uniform_int_distribution<>(0, operandCount - 1)(gen); + int trigType = std::uniform_int_distribution<>(0, 2)(gen); + int angle = std::uniform_int_distribution<>(0, 90)(gen); // 角度范围0-90度 + + // 构建表达式 + question = numbers[0]; + for (size_t i = 0; i < operators.size(); i++) { + // 在指定位置插入三角函数 + if (static_cast(i) == trigPos) { + std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; + question = trigExpr + L" " + operators[i] + L" " + question; + } else { + // 简单的括号逻辑 + if (i < operators.size() - 1 && (operators[i] == L"+" || operators[i] == L"-") && + (operators[i+1] == L"×" || operators[i+1] == L"÷")) { + question = L"(" + question + L")"; + } + question += L" " + operators[i] + L" " + numbers[i + 1]; + } + } + + // 如果三角函数在最后一个位置 + if (trigPos == static_cast(operators.size())) { + std::wstring trigExpr = trigFunctions[trigType] + L"(" + std::to_wstring(angle) + L"°)"; + question = trigExpr + L" " + operators.back() + L" " + question; + } + + return question + L" = ?"; // 只有多个操作数时才在这里添加 = ? + } +} + +//题目查重的实现 +bool QuestionGenerator::IsQuestionUnique(const std::wstring& question) { + return generatedQuestions.find(question) == generatedQuestions.end(); +} + +//生成指定数量题目的实现 +std::vector QuestionGenerator::generateQuestions(int count) { + // 验证题目数量范围 + if (count < 10 || count > 30) { + qDebug() << "QuestionGenerator: 题目数量超出范围(10-30),使用默认值10"; + count = 10; + } + + std::vector questions; + int attempts = 0; + const int maxAttempts = count * 10; // 增加尝试次数 + + for (int i = 0; i < count && attempts < maxAttempts; i++) { + std::wstring question; + + switch(currentGrade) { + case Grade::PRIMARY: + question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + question = generateSeniorQuestion(); + break; + } + + if (IsQuestionUnique(question)) { + questions.push_back(question); + generatedQuestions.insert(question); + qDebug() << "生成题目" << i + 1 << ":" << QString::fromStdWString(question); + } else { + i--; + qDebug() << "题目重复,重新生成"; + } + attempts++; + } + + if (static_cast(questions.size()) < count) { + qDebug() << "QuestionGenerator: 警告:只生成了" << questions.size() << "个唯一题目"; + } + + qDebug() << "QuestionGenerator: 成功生成" << questions.size() << "个题目"; + return questions; +} + +// 计算小学表达式结果 - 改进版本 +double QuestionGenerator::calculatePrimaryExpression(const std::wstring& expr) { + try { + // 简单的表达式计算,支持基础运算 + std::wstring expression = expr; + + // 移除空格 + expression.erase(std::remove(expression.begin(), expression.end(), L' '), expression.end()); + + // 简单的递归计算函数 + std::function calculate = [&](const std::wstring& str) -> double { + if (str.empty()) return 0.0; + + // 处理括号 + if (str.front() == L'(' && str.back() == L')') { + return calculate(str.substr(1, str.length() - 2)); + } + + // 查找最低优先级的运算符 + int parenCount = 0; + int lowestPriority = -1; + int lowestPos = -1; + + for (int i = static_cast(str.length()) - 1; i >= 0; i--) { + wchar_t c = str[i]; + if (c == L')') parenCount++; + else if (c == L'(') parenCount--; + else if (parenCount == 0) { + if ((c == L'+' || c == L'-') && lowestPriority < 1) { + lowestPriority = 1; + lowestPos = i; + } else if ((c == L'×' || c == L'÷') && lowestPriority < 0) { + lowestPriority = 0; + lowestPos = i; + } + } + } + + if (lowestPos != -1) { + std::wstring left = str.substr(0, lowestPos); + std::wstring right = str.substr(lowestPos + 1); + wchar_t op = str[lowestPos]; + + double leftVal = calculate(left); + double rightVal = calculate(right); + + switch(op) { + case L'+': return leftVal + rightVal; + case L'-': return leftVal - rightVal; + case L'×': return leftVal * rightVal; + case L'÷': + if (rightVal == 0) { + qDebug() << "除零错误"; + return 0.0; + } + return leftVal / rightVal; + } + } + + // 没有运算符,直接解析数字 + try { + return std::stod(str); + } catch (...) { + qDebug() << "无法解析数字:" << QString::fromStdWString(str); + return 0.0; + } + }; + + return calculate(expression); + } catch (const std::exception& e) { + qDebug() << "计算小学表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算初中表达式结果 - 改进版本 +double QuestionGenerator::calculateJuniorExpression(const std::wstring& expr) { + try { + std::wstring expression = expr; + + // 处理平方运算 + size_t squarePos = expression.find(L"²"); + while (squarePos != std::wstring::npos) { + // 找到平方的基数 + int start = static_cast(squarePos) - 1; + while (start >= 0 && (std::iswdigit(expression[start]) || expression[start] == L')')) { + start--; + } + if (start >= 0 && expression[start] == L'(') start--; + + std::wstring baseStr = expression.substr(start + 1, squarePos - start - 1); + double base = calculatePrimaryExpression(baseStr); + double result = base * base; + + // 替换平方部分 + expression.replace(start + 1, squarePos - start + 1, std::to_wstring(result)); + squarePos = expression.find(L"²"); + } + + // 处理开方运算 + size_t sqrtPos = expression.find(L"√"); + while (sqrtPos != std::wstring::npos) { + // 找到被开方数 + size_t end = sqrtPos + 1; + while (end < expression.length() && (std::iswdigit(expression[end]) || expression[end] == L'(')) { + end++; + } + + std::wstring radicandStr = expression.substr(sqrtPos + 1, end - sqrtPos - 1); + double radicand = calculatePrimaryExpression(radicandStr); + if (radicand < 0) { + qDebug() << "负数开方错误"; + return 0.0; + } + double result = std::sqrt(radicand); + + // 替换开方部分 + expression.replace(sqrtPos, end - sqrtPos, std::to_wstring(result)); + sqrtPos = expression.find(L"√"); + } + + return calculatePrimaryExpression(expression); + } catch (const std::exception& e) { + qDebug() << "计算初中表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算高中表达式结果 - 改进版本 +double QuestionGenerator::calculateSeniorExpression(const std::wstring& expr) { + try { + std::wstring expression = expr; + + // 处理三角函数 + std::vector trigFuncs = {L"sin", L"cos", L"tan"}; + + for (const auto& func : trigFuncs) { + size_t pos = expression.find(func); + while (pos != std::wstring::npos) { + // 找到括号内的角度 + size_t parenStart = pos + func.length(); + if (parenStart >= expression.length() || expression[parenStart] != L'(') { + break; + } + + size_t parenEnd = expression.find(L')', parenStart); + if (parenEnd == std::wstring::npos) { + break; + } + + std::wstring angleStr = expression.substr(parenStart + 1, parenEnd - parenStart - 1); + // 移除度符号 + if (!angleStr.empty() && angleStr.back() == L'°') { + angleStr.pop_back(); + } + + double angle = std::stod(angleStr); + double result = 0.0; + + // 转换为弧度并计算 + double radians = angle * M_PI / 180.0; + if (func == L"sin") { + result = std::sin(radians); + } else if (func == L"cos") { + result = std::cos(radians); + } else if (func == L"tan") { + if (std::abs(std::cos(radians)) < 1e-10) { + qDebug() << "tan函数计算错误,角度:" << angle; + return 0.0; + } + result = std::tan(radians); + } + + // 替换三角函数部分 + expression.replace(pos, parenEnd - pos + 1, std::to_wstring(result)); + pos = expression.find(func); + } + } + + return calculateJuniorExpression(expression); + } catch (const std::exception& e) { + qDebug() << "计算高中表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算表达式结果 +double QuestionGenerator::calculateExpression(const std::wstring& expression) { + try { + switch(currentGrade) { + case Grade::PRIMARY: + return calculatePrimaryExpression(expression); + case Grade::JUNIOR: + return calculateJuniorExpression(expression); + case Grade::SENIOR: + return calculateSeniorExpression(expression); + default: + return 0.0; + } + } catch (const std::exception& e) { + qDebug() << "计算表达式异常:" << e.what(); + return 0.0; + } +} + +// 计算正确答案 +double QuestionGenerator::calculateCorrectAnswer(const std::wstring& question) { + try { + // 移除 "= ?" 部分 + std::wstring expression = question; + size_t pos = expression.find(L" = ?"); + if (pos != std::wstring::npos) { + expression = expression.substr(0, pos); + } + + qDebug() << "计算题目答案:" << QString::fromStdWString(expression); + double result = calculateExpression(expression); + qDebug() << "计算结果:" << result; + return result; + } catch (const std::exception& e) { + qDebug() << "计算正确答案异常:" << e.what(); + return 0.0; + } +} + +// 生成有意义的选项 +std::vector QuestionGenerator::generateMeaningfulOptions(double correctAnswer) { + try { + std::vector options; + std::uniform_real_distribution dis(-2.0, 2.0); + std::set usedValues; + + // 格式化正确答案 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctAnswer; + std::string correctStr = oss.str(); + // 移除末尾多余的0 + correctStr.erase(correctStr.find_last_not_of('0') + 1, std::string::npos); + if (!correctStr.empty() && correctStr.back() == '.') { + correctStr.pop_back(); + } + + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + options.push_back(wcorrectStr); + usedValues.insert(correctAnswer); + + // 生成有意义的干扰项 + for (int i = 0; i < 3; ++i) { + double variation = dis(gen); + double wrongAnswer = correctAnswer + variation; + + // 确保干扰项与正确答案不同且唯一 + int attempts = 0; + while ((std::abs(wrongAnswer - correctAnswer) < 0.001 || + usedValues.find(wrongAnswer) != usedValues.end()) && + attempts < 10) { + variation = dis(gen); + wrongAnswer = correctAnswer + variation; + attempts++; + } + + usedValues.insert(wrongAnswer); + + std::stringstream wrongOss; + wrongOss << std::fixed << std::setprecision(6) << wrongAnswer; + std::string wrongStr = wrongOss.str(); + // 移除末尾多余的0 + wrongStr.erase(wrongStr.find_last_not_of('0') + 1, std::string::npos); + if (!wrongStr.empty() && wrongStr.back() == '.') { + wrongStr.pop_back(); + } + + std::wstring wwrongStr(wrongStr.begin(), wrongStr.end()); + options.push_back(wwrongStr); + } + + // 随机打乱选项顺序 + std::shuffle(options.begin(), options.end(), gen); + + return options; + } catch (const std::exception& e) { + qDebug() << "生成选项异常:" << e.what(); + // 返回默认选项 + return {L"1.0", L"2.0", L"3.0", L"4.0"}; + } +} + +//生成题目和选项的实现(用于GUI考试) +QuestionGenerator::QuestionWithOptions QuestionGenerator::generateQuestionWithOptions() { + QuestionWithOptions qwo; + + try { + // 生成题目 + switch(currentGrade) { + case Grade::PRIMARY: + qwo.question = generatePrimaryQuestion(); + break; + case Grade::JUNIOR: + qwo.question = generateJuniorQuestion(); + break; + case Grade::SENIOR: + qwo.question = generateSeniorQuestion(); + break; + } + + // 计算正确答案并生成有意义的选项 + double correctAnswer = calculateCorrectAnswer(qwo.question); + qwo.options = generateMeaningfulOptions(correctAnswer); + + // 找到正确答案的索引 + std::stringstream oss; + oss << std::fixed << std::setprecision(6) << correctAnswer; + std::string correctStr = oss.str(); + std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); + + for (size_t i = 0; i < qwo.options.size(); ++i) { + if (qwo.options[i] == wcorrectStr) { + qwo.correctIndex = static_cast(i); + break; + } + } + } catch (const std::exception& e) { + qDebug() << "生成题目和选项异常:" << e.what(); + // 返回默认题目 + qwo.question = L"1 + 1 = ?"; + qwo.options = {L"1.0", L"2.0", L"3.0", L"4.0"}; + qwo.correctIndex = 1; + } + + return qwo; +} + +//清空历史记录的实现 +void QuestionGenerator::clearHistory() { + generatedQuestions.clear(); +} diff --git a/src/back/questiongenerator.h b/src/back/questiongenerator.h new file mode 100644 index 0000000..487b79c --- /dev/null +++ b/src/back/questiongenerator.h @@ -0,0 +1,78 @@ +#ifndef QUESTIONGENERATOR_H +#define QUESTIONGENERATOR_H + +#include "user.h" +#include +#include +#include +#include +#include +#include +#include + +//题目生成器类,根据年级生成不同难度的数学题目 +//支持小学、初中、高中三个级别的题目生成,包含查重功能。 +class QuestionGenerator { +private: + Grade currentGrade; + std::set generatedQuestions; + std::random_device rd; + std::mt19937 gen; + + //生成小学级别的基础算术题目 + std::wstring generatePrimaryQuestion(); + + //生成初中级别的题目,包含平方或开根号 + std::wstring generateJuniorQuestion(); + + //生成高中级别的题目,包含三角函数 + std::wstring generateSeniorQuestion(); + + //生成指定范围内的随机整数 + std::wstring generateNumber(int min, int max); + + //随机生成算术运算符 + std::wstring generateOperator(); + + //检查题目是否已生成过(查重) + bool IsQuestionUnique(const std::wstring& question); + + //括号需求判断 + bool needsParentheses(const std::wstring& expr, const std::wstring& nextOp); + + // 计算表达式结果 + double calculateExpression(const std::wstring& expression); + double calculatePrimaryExpression(const std::wstring& expr); + double calculateJuniorExpression(const std::wstring& expr); + double calculateSeniorExpression(const std::wstring& expr); + +public: + //构造函数,初始化题目生成器 + QuestionGenerator(); + + //设置当前题目生成难度级别 + void setGrade(Grade grade); + + //生成指定数量的唯一数学题目 + std::vector generateQuestions(int count); + + //生成题目和选项(用于GUI) + struct QuestionWithOptions { + std::wstring question; + std::vector options; + int correctIndex; + }; + + QuestionWithOptions generateQuestionWithOptions(); + + //清空已生成题目的历史记录 + void clearHistory(); + + // 计算正确答案 + double calculateCorrectAnswer(const std::wstring& question); + + // 生成有意义的选项 + std::vector generateMeaningfulOptions(double correctAnswer); +}; + +#endif diff --git a/src/back/user.cpp b/src/back/user.cpp new file mode 100644 index 0000000..65b0f8d --- /dev/null +++ b/src/back/user.cpp @@ -0,0 +1,41 @@ +#include "user.h" +#include + +//用户构造函数的实现 +User::User(const std::wstring& name, const std::wstring& pass, Grade grade) + : username(name), password(pass), gradeType(grade) {} + +//用户验证的实现 +bool User::check(const std::wstring& name, const std::wstring& pass) const { + return (username == name && password == pass); +} + +//获取用户名的实现 +std::wstring User::getUsername() const { + return username; +} + +//获取密码的实现 +std::wstring User::getPassword() const { + return password; +} + +//获取年级枚举值的实现 +Grade User::getGrade() const { + return gradeType; +} + +//获取年级字符串的实现 +std::wstring User::getGradeString() const { + switch(gradeType) { + case Grade::PRIMARY: return L"小学"; + case Grade::JUNIOR: return L"初中"; + case Grade::SENIOR: return L"高中"; + default: return L"未知"; + } +} + +//设置密码的实现 +void User::setPassword(const std::wstring& newPassword) { + password = newPassword; +} \ No newline at end of file diff --git a/src/back/user.h b/src/back/user.h new file mode 100644 index 0000000..554141d --- /dev/null +++ b/src/back/user.h @@ -0,0 +1,41 @@ +#ifndef USER_H +#define USER_H + +#include + +//年级枚举,表示用户所属的学段级别 +//用于区分不同学段的题目难度和生成规则 +enum class Grade { PRIMARY, JUNIOR, SENIOR }; + +//用户信息类,封装用户认证数据和年级信息 +//存储用户名、密码和年级类型,提供验证和查询接口。 +class User { +private: + std::wstring username; + std::wstring password; + Grade gradeType; + +public: + //构造函数,初始化用户信息 + User(const std::wstring& name, const std::wstring& pass, Grade grade); + + //验证用户名和密码是否匹配 + bool check(const std::wstring& name, const std::wstring& pass) const; + + //获取用户名 + std::wstring getUsername() const; + + //获取密码 + std::wstring getPassword() const; + + //获取年级枚举值 + Grade getGrade() const; + + //获取年级的中文描述 + std::wstring getGradeString() const; + + //设置密码 + void setPassword(const std::wstring& newPassword); +}; + +#endif \ No newline at end of file diff --git a/src/back/usermanage.cpp b/src/back/usermanage.cpp new file mode 100644 index 0000000..4fcba32 --- /dev/null +++ b/src/back/usermanage.cpp @@ -0,0 +1,431 @@ +#include "usermanage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // 添加进程支持 +#include // 添加目录支持 + +// 初始化用户数据的实现 +void UserManager::initializeUsers() { + // 如果文件中有用户数据,就不初始化默认用户 + if (loadUsersFromFile()) { + qDebug() << "UserManager: 从文件加载用户数据成功"; + return; + } + + qDebug() << "UserManager: 初始化默认用户数据"; + + // 初始化默认测试用户(使用英文用户名避免编码问题) + users.emplace_back(L"zhangsan1", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"zhangsan2", L"Abc123", Grade::PRIMARY); + users.emplace_back(L"zhangsan3", L"Abc123", Grade::PRIMARY); + + users.emplace_back(L"lisi1", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"lisi2", L"Abc123", Grade::JUNIOR); + users.emplace_back(L"lisi3", L"Abc123", Grade::JUNIOR); + + users.emplace_back(L"wangwu1", L"Abc123", Grade::SENIOR); + users.emplace_back(L"wangwu2", L"Abc123", Grade::SENIOR); + users.emplace_back(L"wangwu3", L"Abc123", Grade::SENIOR); + + // 保存到文件 + if (saveUsersToFile()) { + qDebug() << "UserManager: 默认用户数据保存成功"; + } else { + qDebug() << "UserManager: 默认用户数据保存失败"; + } +} + +// 用户管理器的构造实现 +UserManager::UserManager() { + qDebug() << "UserManager: 构造函数调用"; + initializeUsers(); +} + +// 用户认证的实现 +User* UserManager::authenticateUser(const std::wstring& username, const std::wstring& password) { + qDebug() << "UserManager: 尝试认证用户:" << QString::fromStdWString(username); + + // 重新加载用户数据,确保包含新注册的用户 + loadUsersFromFile(); + + for (auto& user : users) { + if (user.check(username, password)) { + qDebug() << "UserManager: 用户认证成功"; + return &user; + } + } + + qDebug() << "UserManager: 用户认证失败"; + return nullptr; +} + +// 解析用户类型的实现 +Grade UserManager::parseUserType(const std::wstring& type) { + if (type == L"小学" || type == L"Primary") return Grade::PRIMARY; + if (type == L"初中" || type == L"Junior") return Grade::JUNIOR; + if (type == L"高中" || type == L"Senior") return Grade::SENIOR; + return Grade::PRIMARY; +} + +// 注册新用户的实现 +bool UserManager::registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade) { + qDebug() << "UserManager: 尝试注册用户:" << QString::fromStdWString(username) << "年级:" << QString::fromStdWString(grade); + + // 重新加载用户数据,确保检查最新的用户名 + loadUsersFromFile(); + + // 检查用户名是否已存在 + if (isUsernameExists(username)) { + qDebug() << "UserManager: 用户名已存在"; + return false; + } + + Grade userGrade = parseUserType(grade); + users.emplace_back(username, password, userGrade); + + qDebug() << "UserManager: 用户添加到内存,准备保存到文件"; + + // 保存到文件 + bool result = saveUsersToFile(); + if (result) { + qDebug() << "UserManager: 用户注册成功"; + // 重新加载确保数据同步 + loadUsersFromFile(); + } else { + qDebug() << "UserManager: 用户注册失败 - 文件保存失败"; + } + + return result; +} + +// 修改用户密码的实现 +bool UserManager::changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword) { + for (auto& user : users) { + if (user.check(username, oldPassword)) { + user.setPassword(newPassword); + return saveUsersToFile(); + } + } + return false; +} + +// 检查用户名是否存在的实现 +bool UserManager::isUsernameExists(const std::wstring& username) { + for (const auto& user : users) { + if (user.getUsername() == username) { + return true; + } + } + return false; +} + +// 从文件加载用户数据的实现 +bool UserManager::loadUsersFromFile() { + std::ifstream file(userFile); + if (!file.is_open()) { + qDebug() << "UserManager: 无法打开用户文件:" << userFile.c_str(); + return false; + } + + users.clear(); + std::string line; + int userCount = 0; + + while (std::getline(file, line)) { + std::istringstream iss(line); + std::string username, password, gradeStr; + + if (iss >> username >> password >> gradeStr) { + // 转换为宽字符串 + std::wstring wusername, wpassword, wgradeStr; + for (char c : username) wusername += wchar_t(c); + for (char c : password) wpassword += wchar_t(c); + for (char c : gradeStr) wgradeStr += wchar_t(c); + + Grade grade = parseUserType(wgradeStr); + users.emplace_back(wusername, wpassword, grade); + userCount++; + qDebug() << "加载用户:" << QString::fromStdWString(wusername) << "年级:" << QString::fromStdWString(wgradeStr); + } + } + + file.close(); + qDebug() << "UserManager: 从文件加载了" << userCount << "个用户"; + return !users.empty(); +} + +// 保存用户数据到文件的实现 +bool UserManager::saveUsersToFile() { + std::ofstream file(userFile); + if (!file.is_open()) { + qDebug() << "UserManager: 无法创建用户文件:" << userFile.c_str(); + return false; + } + + int savedCount = 0; + for (const auto& user : users) { + std::wstring username = user.getUsername(); + std::wstring password = user.getPassword(); + std::wstring gradeStr = user.getGradeString(); + + // 转换为普通字符串 + std::string username_str, password_str, grade_str; + for (wchar_t wc : username) username_str += static_cast(wc); + for (wchar_t wc : password) password_str += static_cast(wc); + for (wchar_t wc : gradeStr) grade_str += static_cast(wc); + + file << username_str << " " << password_str << " " << grade_str << "\n"; + savedCount++; + } + + file.close(); + qDebug() << "UserManager: 保存了" << savedCount << "个用户到文件"; + return true; +} + +// 生成验证码的实现 - 修复为每次都不同 +std::wstring UserManager::generateVerificationCode(int length) { + const std::wstring chars = L"0123456789"; + std::wstring code; + + // 使用当前时间作为随机种子,确保每次不同 + std::random_device rd; + std::mt19937 gen(rd() + static_cast(std::time(nullptr))); + std::uniform_int_distribution<> dis(0, chars.length() - 1); + + for (int i = 0; i < length; ++i) { + code += chars[dis(gen)]; + } + + qDebug() << "UserManager: 生成验证码:" << QString::fromStdWString(code); + return code; +} + +// 发送验证码的实现 - 添加真实邮件发送功能 +bool UserManager::sendVerificationCode(const std::wstring& email, std::wstring& generatedCode) { + qDebug() << "UserManager: 发送验证码到:" << QString::fromStdWString(email); + + // 生成验证码 + generatedCode = generateVerificationCode(); + + // 设置过期时间(10分钟) + auto expiryTime = std::chrono::system_clock::now() + std::chrono::minutes(10); + + // 存储验证码信息 + emailVerifications[email] = {generatedCode, expiryTime, false}; + + qDebug() << "UserManager: 验证码已存储,有效期10分钟"; + + // 尝试发送真实邮件 + if (sendRealEmail(email, generatedCode)) { + qDebug() << "UserManager: 验证码邮件发送成功"; + return true; + } else { + // 如果真实邮件发送失败,使用控制台输出 + std::wcout << L"[邮件模拟] 发送到: " << email << L" 的验证码: " << generatedCode << std::endl; + qDebug() << "UserManager: 真实邮件发送失败,使用模拟模式"; + + // 在调试窗口显示验证码信息 + QString qEmail = QString::fromStdWString(email); + QString qCode = QString::fromStdWString(generatedCode); + qDebug() << "=== 邮件发送模拟 ==="; + qDebug() << "收件人:" << qEmail; + qDebug() << "验证码:" << qCode; + qDebug() << "=== 邮件发送模拟 ==="; + + return true; + } +} + +// 验证邮箱验证码的实现 +bool UserManager::verifyEmailCode(const std::wstring& email, const std::wstring& code) { + qDebug() << "UserManager: 验证邮箱验证码,邮箱:" << QString::fromStdWString(email) + << "验证码:" << QString::fromStdWString(code); + + auto it = emailVerifications.find(email); + if (it == emailVerifications.end()) { + qDebug() << "UserManager: 没有找到该邮箱的验证码记录"; + return false; // 没有找到该邮箱的验证码记录 + } + + EmailVerification& verification = it->second; + + // 检查验证码是否已使用 + if (verification.used) { + qDebug() << "UserManager: 验证码已被使用"; + return false; + } + + // 检查验证码是否过期 + if (std::chrono::system_clock::now() > verification.expiryTime) { + qDebug() << "UserManager: 验证码已过期"; + emailVerifications.erase(it); + return false; + } + + // 检查验证码是否匹配 + if (verification.code == code) { + qDebug() << "UserManager: 验证码验证成功"; + verification.used = true; // 标记为已使用 + return true; + } + + qDebug() << "UserManager: 验证码不匹配"; + return false; +} + +// 真实邮件发送功能 - 仅使用 PowerShell +bool UserManager::sendRealEmail(const std::wstring& email, const std::wstring& code) { + QString qEmail = QString::fromStdWString(email); + QString qCode = QString::fromStdWString(code); + + qDebug() << "尝试使用 PowerShell 发送邮件到:" << qEmail; + + return sendEmailViaPowerShell(qEmail, qCode); +} + +// 辅助函数:安全地转义字符串中的单引号 +QString escapeSingleQuotes(const QString& input) { + QString result = input; + return result.replace("'", "''"); +} + +// 使用 PowerShell Send-MailMessage 发送邮件 +bool UserManager::sendEmailViaPowerShell(const QString& toEmail, const QString& code) { + qDebug() << "使用 PowerShell 发送邮件..."; + + // ==================== 配置区域 ==================== + // 请根据你的邮箱服务商修改以下配置: + + QString smtpServer = "smtp.qq.com"; // SMTP服务器 + int smtpPort = 587; // 端口号 + QString fromEmail = "1453386832@qq.com"; // ⬅️ 修改:发件人邮箱 + QString username = "1453386832@qq.com"; // ⬅️ 修改:邮箱账号 + QString password = "nijuqetihpojffag"; // ⬅️ 修改:邮箱授权码 + + /* + // 其他邮箱配置示例: + + // 163邮箱配置: + // QString smtpServer = "smtp.163.com"; + // int smtpPort = 25; // 或 587 + // QString fromEmail = "your_email@163.com"; + // QString username = "your_email@163.com"; + // QString password = "your_163_authorization_code"; + + // Gmail配置: + // QString smtpServer = "smtp.gmail.com"; + // int smtpPort = 587; + // QString fromEmail = "your_email@gmail.com"; + // QString username = "your_email@gmail.com"; + // QString password = "your_gmail_app_password"; + */ + // ==================== 配置结束 ==================== + + QString subject = "Math Learning Software - Verification Code"; + QString body = QString( + "Dear User:\n\n" + "You are registering for Math Learning Software. Your verification code is: %1\n\n" + "The verification code is valid for 10 minutes. Please complete your registration as soon as possible.\n\n" + "If this was not your operation, please ignore this email.\n\n" + "Math Learning Software Team" + ).arg(code); + + // 安全地转义所有字符串 + QString safePassword = escapeSingleQuotes(password); + QString safeUsername = escapeSingleQuotes(username); + QString safeFromEmail = escapeSingleQuotes(fromEmail); + QString safeToEmail = escapeSingleQuotes(toEmail); + QString safeSubject = escapeSingleQuotes(subject); + QString safeBody = escapeSingleQuotes(body); + QString safeSmtpServer = escapeSingleQuotes(smtpServer); + + // 构建 PowerShell 命令 + QString powerShellScript = QString( + "$secpasswd = ConvertTo-SecureString \"%1\" -AsPlainText -Force\n" + "$credential = New-Object System.Management.Automation.PSCredential(\"%2\", $secpasswd)\n" + "Send-MailMessage -From '%3' -To '%4' -Subject '%5' -Body '%6' -SmtpServer '%7' -Port %8 -Credential $credential -UseSsl" + ).arg(safePassword, + safeUsername, + safeFromEmail, + safeToEmail, + safeSubject, + safeBody, + safeSmtpServer, + QString::number(smtpPort)); + + // 创建临时 PowerShell 脚本文件 + QString tempScriptFile = QDir::tempPath() + "/send_email.ps1"; + QFile scriptFile(tempScriptFile); + + if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "无法创建临时 PowerShell 脚本文件"; + return false; + } + + QTextStream out(&scriptFile); + out << powerShellScript; + scriptFile.close(); + + qDebug() << "执行 PowerShell 脚本..."; + + // 执行 PowerShell 脚本 + QProcess process; + process.start("powershell", QStringList() << "-ExecutionPolicy" << "Bypass" << "-File" << tempScriptFile); + + if (!process.waitForStarted(10000)) { + qDebug() << "无法启动 PowerShell 进程"; + QFile::remove(tempScriptFile); + return false; + } + + if (!process.waitForFinished(60000)) { // 等待60秒 + qDebug() << "PowerShell 进程超时"; + process.kill(); + QFile::remove(tempScriptFile); + return false; + } + + int exitCode = process.exitCode(); + QByteArray output = process.readAllStandardOutput(); + QByteArray error = process.readAllStandardError(); + + qDebug() << "PowerShell 退出代码:" << exitCode; + + if (!output.isEmpty()) { + qDebug() << "PowerShell 输出:" << output; + } + + if (!error.isEmpty()) { + qDebug() << "PowerShell 错误:" << error; + } + + // 清理临时文件 + QFile::remove(tempScriptFile); + + if (exitCode == 0) { + qDebug() << "邮件发送成功!"; + return true; + } else { + qDebug() << "邮件发送失败,退出代码:" << exitCode; + + // 提供详细的错误信息 + if (error.contains("Authentication")) { + qDebug() << "错误:认证失败,请检查用户名和密码"; + } else if (error.contains("Connection")) { + qDebug() << "错误:连接失败,请检查SMTP服务器和端口"; + } else if (error.contains("SSL")) { + qDebug() << "错误:SSL连接失败"; + } + + return false; + } +} diff --git a/src/back/usermanage.h b/src/back/usermanage.h new file mode 100644 index 0000000..9427a6e --- /dev/null +++ b/src/back/usermanage.h @@ -0,0 +1,75 @@ +#ifndef USERMANAGE_H +#define USERMANAGE_H + +#include "user.h" +#include +#include +#include +#include +#include +#include +#include +#include // 添加进程支持 + +// 邮箱验证码结构 +struct EmailVerification { + std::wstring code; + std::chrono::system_clock::time_point expiryTime; + bool used; +}; + +// 用户管理器类,负责用户数据的初始化和认证管理 +class UserManager { +private: + std::vector users; + const std::string userFile = "users.dat"; + + // 邮箱验证码存储 + std::map emailVerifications; + + //初始化预定义用户数据 + void initializeUsers(); + + //从文件加载用户数据 + bool loadUsersFromFile(); + + //保存用户数据到文件 + bool saveUsersToFile(); + + // 真实邮件发送 + bool sendRealEmail(const std::wstring& email, const std::wstring& code); + + // PowerShell 邮件发送方法 + bool sendEmailViaPowerShell(const QString& toEmail, const QString& code); + +public: + //构造函数,自动初始化用户数据 + UserManager(); + + //用户认证,验证用户名和密码 + User* authenticateUser(const std::wstring& username, const std::wstring& password); + + //将中文年级字符串解析为年级枚举值 + Grade parseUserType(const std::wstring& type); + + //注册新用户 + bool registerUser(const std::wstring& username, const std::wstring& password, const std::wstring& grade); + + //修改用户密码 + bool changePassword(const std::wstring& username, const std::wstring& oldPassword, const std::wstring& newPassword); + + //检查用户名是否存在 + bool isUsernameExists(const std::wstring& username); + + // 邮箱验证相关方法 + bool sendVerificationCode(const std::wstring& email, std::wstring& generatedCode); + bool verifyEmailCode(const std::wstring& email, const std::wstring& code); + + // 生成随机验证码 + std::wstring generateVerificationCode(int length = 6); + + //获取所有用户(用于测试) + std::vector& getUsers() { return users; } +}; + +#endif diff --git a/src/frontend/examwidget.cpp b/src/frontend/examwidget.cpp deleted file mode 100644 index ab8d906..0000000 --- a/src/frontend/examwidget.cpp +++ /dev/null @@ -1,376 +0,0 @@ -#include "examwidget.h" -#include -#include -#include -#include -#include -#include -#include -#include - -ExamWidget::ExamWidget(QWidget *parent) : QWidget(parent), currentQuestion(0), totalQuestions(0) -{ - QVBoxLayout *mainLayout = new QVBoxLayout(this); - - // 进度标签 - progressLabel = new QLabel(); - progressLabel->setAlignment(Qt::AlignCenter); - progressLabel->setStyleSheet("font-size: 16px; font-weight: bold; margin: 20px; color: #2c3e50;"); - - // 题目区域 - QGroupBox *questionGroup = new QGroupBox("题目"); - questionGroup->setStyleSheet("QGroupBox {" - "font-size: 16px;" - "font-weight: bold;" - "margin-top: 10px;" - "}" - "QGroupBox::title {" - "subcontrol-origin: margin;" - "subcontrol-position: top center;" - "padding: 0 5px;" - "}"); - - QVBoxLayout *questionLayout = new QVBoxLayout(questionGroup); - - questionLabel = new QLabel(); - questionLabel->setWordWrap(true); - questionLabel->setStyleSheet("font-size: 18px; margin: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 5px;"); - questionLabel->setAlignment(Qt::AlignCenter); - questionLabel->setMinimumHeight(80); - - questionLayout->addWidget(questionLabel); - - // 选项区域 - QGroupBox *optionsGroup = new QGroupBox("请选择答案"); - optionsGroup->setStyleSheet("QGroupBox {" - "font-size: 16px;" - "font-weight: bold;" - "margin-top: 10px;" - "}" - "QGroupBox::title {" - "subcontrol-origin: margin;" - "subcontrol-position: top center;" - "padding: 0 5px;" - "}"); - - QVBoxLayout *optionsLayout = new QVBoxLayout(optionsGroup); - optionGroup = new QButtonGroup(this); - - for (int i = 0; i < 4; ++i) { - optionButtons[i] = new QRadioButton(); - optionButtons[i]->setStyleSheet("QRadioButton {" - "font-size: 14px;" - "margin: 8px;" - "padding: 8px;" - "}" - "QRadioButton::indicator {" - "width: 20px;" - "height: 20px;" - "}"); - optionGroup->addButton(optionButtons[i], i); - optionsLayout->addWidget(optionButtons[i]); - } - - // 按钮区域 - QWidget *buttonWidget = new QWidget(); - QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); - - // 上一题按钮 - previousButton = new QPushButton("上一题"); - previousButton->setStyleSheet("QPushButton {" - "background-color: #95a5a6;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #7f8c8d;" - "}" - "QPushButton:disabled {" - "background-color: #bdc3c7;" - "color: #7f8c8d;" - "}"); - previousButton->setFixedWidth(120); - previousButton->setEnabled(false); // 第一题时禁用 - - nextButton = new QPushButton("下一题"); - nextButton->setStyleSheet("QPushButton {" - "background-color: #3498db;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #2980b9;" - "}"); - nextButton->setFixedWidth(120); - - submitButton = new QPushButton("提交试卷"); - submitButton->setStyleSheet("QPushButton {" - "background-color: #e74c3c;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #c0392b;" - "}"); - submitButton->setFixedWidth(120); - submitButton->setVisible(false); - - buttonLayout->addStretch(); - buttonLayout->addWidget(previousButton); - buttonLayout->addSpacing(10); - buttonLayout->addWidget(nextButton); - buttonLayout->addSpacing(10); - buttonLayout->addWidget(submitButton); - buttonLayout->addStretch(); - - // 添加到主布局 - mainLayout->addWidget(progressLabel); - mainLayout->addWidget(questionGroup); - mainLayout->addWidget(optionsGroup); - mainLayout->addSpacing(20); - mainLayout->addWidget(buttonWidget); - mainLayout->addStretch(); - - // 连接信号槽 - connect(previousButton, &QPushButton::clicked, this, &ExamWidget::onPreviousClicked); - connect(nextButton, &QPushButton::clicked, this, &ExamWidget::onNextClicked); - connect(submitButton, &QPushButton::clicked, this, &ExamWidget::onSubmitClicked); -} - -void ExamWidget::startExam(Grade grade, int questionCount) -{ - try { - qDebug() << "ExamWidget: 开始考试,年级:" << static_cast(grade) << "题目数量:" << questionCount; - - questionGenerator.setGrade(grade); - questions = questionGenerator.generateQuestions(questionCount); - totalQuestions = static_cast(questions.size()); - currentQuestion = 0; - userAnswers.clear(); - correctAnswers.clear(); - questionOptions.clear(); - - if (questions.empty()) { - qDebug() << "ExamWidget: 题目生成失败"; - QMessageBox::warning(this, "错误", "题目生成失败,请重试"); - return; - } - - qDebug() << "ExamWidget: 成功生成" << totalQuestions << "个题目"; - - // 预计算所有题目的正确答案和选项 - for (int i = 0; i < totalQuestions; ++i) { - try { - double correctValue = questionGenerator.calculateCorrectAnswer(questions[i]); - - // 生成选项并保存 - std::vector options = questionGenerator.generateMeaningfulOptions(correctValue); - questionOptions.push_back(options); - - // 找到正确答案的索引 - int correctIndex = 0; - bool found = false; - - for (size_t j = 0; j < options.size(); ++j) { - try { - double optionValue = std::stod(options[j]); - if (std::abs(optionValue - correctValue) < 0.0001) { - correctIndex = static_cast(j); - found = true; - break; - } - } catch (...) { - // 如果转换失败,使用字符串比较 - std::stringstream oss; - oss << std::fixed << std::setprecision(6) << correctValue; - std::string correctStr = oss.str(); - std::wstring wcorrectStr(correctStr.begin(), correctStr.end()); - - if (options[j] == wcorrectStr) { - correctIndex = static_cast(j); - found = true; - break; - } - } - } - - if (!found) { - qDebug() << "警告:未找到第" << i << "题的正确答案在选项中"; - } - - correctAnswers.push_back(correctIndex); - qDebug() << "ExamWidget: 第" << i << "题正确答案:" << correctValue << "索引:" << correctIndex; - - } catch (const std::exception& e) { - qDebug() << "ExamWidget: 计算第" << i << "题答案异常:" << e.what(); - correctAnswers.push_back(0); // 默认第一个选项为正确答案 - questionOptions.push_back({L"1.000000", L"2.000000", L"3.000000", L"4.000000"}); - } - } - - showQuestion(0); - updateButtonStates(); // 更新按钮状态 - qDebug() << "ExamWidget: 考试初始化完成"; - } catch (const std::exception& e) { - qDebug() << "ExamWidget: 开始考试异常:" << e.what(); - QMessageBox::critical(this, "错误", QString("考试初始化失败: %1").arg(e.what())); - } -} - -void ExamWidget::showQuestion(int index) -{ - if (index < totalQuestions) { - currentQuestion = index; - progressLabel->setText(QString("第 %1 题 / 共 %2 题").arg(index + 1).arg(totalQuestions)); - - // 显示题目 - QString questionText = QString::fromStdWString(questions[index]); - questionLabel->setText(questionText); - - // 设置选项文本 - 使用预先生成的选项 - if (static_cast(index) < questionOptions.size()) { - for (int i = 0; i < 4; ++i) { - if (i < static_cast(questionOptions[index].size())) { - optionButtons[i]->setText(QString::fromStdWString(questionOptions[index][i])); - } else { - optionButtons[i]->setText(QString("选项 %1").arg(i + 1)); - } - } - } - - // 清除选择 - optionGroup->setExclusive(false); - for (int i = 0; i < 4; ++i) { - optionButtons[i]->setChecked(false); - } - optionGroup->setExclusive(true); - - // 恢复之前的选择(如果有) - if (static_cast(index) < userAnswers.size()) { - int previousAnswer = userAnswers[index]; - if (previousAnswer >= 0 && previousAnswer < 4) { - optionButtons[previousAnswer]->setChecked(true); - } - } - - // 更新按钮状态 - updateButtonStates(); - } -} - -void ExamWidget::updateButtonStates() -{ - // 更新上一题按钮状态 - previousButton->setEnabled(currentQuestion > 0); - - // 更新下一题和提交按钮状态 - nextButton->setVisible(currentQuestion < totalQuestions - 1); - submitButton->setVisible(currentQuestion == totalQuestions - 1); -} - -void ExamWidget::generateOptions() -{ - // 这个方法现在不再使用,因为选项在startExam中已经预生成 - // 保留这个方法是为了保持接口兼容性 -} - -void ExamWidget::onPreviousClicked() -{ - // 保存当前答案 - int selected = optionGroup->checkedId(); - if (selected != -1) { - if (static_cast(currentQuestion) >= userAnswers.size()) { - userAnswers.resize(currentQuestion + 1); - } - userAnswers[currentQuestion] = selected; - } - - // 显示上一题 - if (currentQuestion > 0) { - showQuestion(currentQuestion - 1); - } -} - -void ExamWidget::onNextClicked() -{ - int selected = optionGroup->checkedId(); - if (selected == -1) { - QMessageBox::warning(this, "提示", "请选择一个答案"); - return; - } - - // 保存当前答案 - if (static_cast(currentQuestion) >= userAnswers.size()) { - userAnswers.resize(currentQuestion + 1); - } - userAnswers[currentQuestion] = selected; - - showQuestion(currentQuestion + 1); -} - -void ExamWidget::onSubmitClicked() -{ - int selected = optionGroup->checkedId(); - if (selected == -1) { - QMessageBox::warning(this, "提示", "请选择一个答案"); - return; - } - - // 保存最后一题的答案 - if (static_cast(currentQuestion) >= userAnswers.size()) { - userAnswers.resize(currentQuestion + 1); - } - userAnswers[currentQuestion] = selected; - - // 计算分数 - 使用预先生成的选项进行比较 - int score = 0; - for (size_t i = 0; i < userAnswers.size(); ++i) { - if (i < correctAnswers.size() && i < questionOptions.size()) { - if (userAnswers[i] >= 0 && userAnswers[i] < static_cast(questionOptions[i].size())) { - std::wstring selectedOption = questionOptions[i][userAnswers[i]]; - - try { - double selectedValue = std::stod(selectedOption); - double correctValue = questionGenerator.calculateCorrectAnswer(questions[i]); - - qDebug() << "第" << i << "题 - 选择值:" << selectedValue - << "正确答案:" << correctValue - << "差值:" << std::abs(selectedValue - correctValue); - - if (std::abs(selectedValue - correctValue) < 1e-5) { - score++; - qDebug() << "第" << i << "题回答正确"; - } else { - qDebug() << "第" << i << "题回答错误,选择值:" << selectedValue - << "正确答案:" << correctValue; - } - } catch (const std::exception& e) { - qDebug() << "第" << i << "题数值转换异常:" << e.what(); - // 回退到索引比较 - if (userAnswers[i] == correctAnswers[i]) { - score++; - qDebug() << "第" << i << "题通过索引比较回答正确"; - } else { - qDebug() << "第" << i << "题通过索引比较回答错误"; - } - } - } else { - qDebug() << "第" << i << "题用户答案索引越界:" << userAnswers[i]; - } - } else { - qDebug() << "第" << i << "题数据不完整"; - } - } - - qDebug() << "ExamWidget: 考试完成,分数:" << score << "/" << totalQuestions; - emit examFinished(score, totalQuestions); -} diff --git a/src/frontend/examwidget.h b/src/frontend/examwidget.h deleted file mode 100644 index e741829..0000000 --- a/src/frontend/examwidget.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef EXAMWIDGET_H -#define EXAMWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "questiongenerator.h" -#include "user.h" - -class ExamWidget : public QWidget -{ - Q_OBJECT - -public: - explicit ExamWidget(QWidget *parent = nullptr); - void startExam(Grade grade, int questionCount); - -signals: - void examFinished(int score, int total); - -private slots: - void onNextClicked(); - void onPreviousClicked(); // 上一题 - void onSubmitClicked(); - -private: - void showQuestion(int index); - void generateOptions(); - void updateButtonStates(); // 更新按钮状态 - - QLabel *questionLabel; - QLabel *progressLabel; - QButtonGroup *optionGroup; - QRadioButton *optionButtons[4]; - QPushButton *previousButton; // 上一题按钮 - QPushButton *nextButton; - QPushButton *submitButton; - - QuestionGenerator questionGenerator; - std::vector questions; - std::vector userAnswers; - std::vector correctAnswers; - std::vector> questionOptions; - - int currentQuestion; - int totalQuestions; -}; - -#endif diff --git a/src/frontend/loginwidget.cpp b/src/frontend/loginwidget.cpp deleted file mode 100644 index 6a1abb0..0000000 --- a/src/frontend/loginwidget.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "loginwidget.h" -#include -#include -#include -#include -#include - -LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent) -{ - // 设置最小尺寸 - setMinimumSize(400, 400); - - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setSpacing(20); - layout->setContentsMargins(50, 50, 50, 50); - - // 标题 - QLabel *titleLabel = new QLabel("数学学习软件 - 登录"); - titleLabel->setAlignment(Qt::AlignCenter); - titleLabel->setStyleSheet("font-size: 24px; font-weight: bold; margin: 20px; color: #2c3e50;"); - - // 用户名输入 - QLabel *usernameLabel = new QLabel("用户名:"); - usernameLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - usernameEdit = new QLineEdit(); - usernameEdit->setPlaceholderText("请输入用户名"); - usernameEdit->setMinimumHeight(35); - usernameEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 密码输入 - QLabel *passwordLabel = new QLabel("密码:"); - passwordLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - passwordEdit = new QLineEdit(); - passwordEdit->setEchoMode(QLineEdit::Password); - passwordEdit->setPlaceholderText("请输入密码"); - passwordEdit->setMinimumHeight(35); - passwordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 按钮区域 - QWidget *buttonWidget = new QWidget(); - QVBoxLayout *buttonLayout = new QVBoxLayout(buttonWidget); - buttonLayout->setSpacing(15); - - loginButton = new QPushButton("登录"); - loginButton->setMinimumHeight(40); - loginButton->setStyleSheet("QPushButton {" - "background-color: #3498db;" - "color: white;" - "border: none;" - "font-size: 16px;" - "font-weight: bold;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #2980b9;" - "}"); - - registerButton = new QPushButton("注册新账号"); - registerButton->setMinimumHeight(40); - registerButton->setStyleSheet("QPushButton {" - "background-color: #95a5a6;" - "color: white;" - "border: none;" - "font-size: 16px;" - "font-weight: bold;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #7f8c8d;" - "}"); - - // 添加退出按钮 - QPushButton *exitButton = new QPushButton("退出系统"); - exitButton->setMinimumHeight(40); - exitButton->setStyleSheet("QPushButton {" - "background-color: #e74c3c;" - "color: white;" - "border: none;" - "font-size: 16px;" - "font-weight: bold;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #c0392b;" - "}"); - - // 测试账号提示 - QLabel *testAccountLabel = new QLabel("测试账号: zhangsan1 / lisi1 / wangwu1 密码: Abc123"); - testAccountLabel->setStyleSheet("font-size: 12px; color: #7f8c8d; margin-top: 10px;"); - testAccountLabel->setAlignment(Qt::AlignCenter); - - buttonLayout->addWidget(loginButton); - buttonLayout->addWidget(registerButton); - buttonLayout->addWidget(exitButton); - buttonLayout->addWidget(testAccountLabel); - - // 添加到布局 - layout->addWidget(titleLabel); - layout->addSpacing(30); - layout->addWidget(usernameLabel); - layout->addWidget(usernameEdit); - layout->addWidget(passwordLabel); - layout->addWidget(passwordEdit); - layout->addSpacing(30); - layout->addWidget(buttonWidget); - - // 连接信号槽 - connect(loginButton, &QPushButton::clicked, this, &LoginWidget::onLoginClicked); - connect(registerButton, &QPushButton::clicked, this, &LoginWidget::onRegisterClicked); - connect(exitButton, &QPushButton::clicked, qApp, &QApplication::quit); - - // 回车键登录 - connect(usernameEdit, &QLineEdit::returnPressed, this, &LoginWidget::onLoginClicked); - connect(passwordEdit, &QLineEdit::returnPressed, this, &LoginWidget::onLoginClicked); - - qDebug() << "LoginWidget: 初始化完成"; -} - -void LoginWidget::onLoginClicked() -{ - QString username = usernameEdit->text().trimmed(); - QString password = passwordEdit->text(); - - qDebug() << "LoginWidget: 尝试登录,用户名:" << username; - - if (username.isEmpty() || password.isEmpty()) { - QMessageBox::warning(this, "输入错误", "请输入用户名和密码"); - return; - } - - User* user = userManager.authenticateUser(username.toStdWString(), - password.toStdWString()); - if (user) { - qDebug() << "LoginWidget: 登录成功"; - emit loginSuccess(user); - } else { - qDebug() << "LoginWidget: 登录失败"; - QMessageBox::warning(this, "登录失败", "用户名或密码错误"); - } -} - -void LoginWidget::onRegisterClicked() -{ - qDebug() << "LoginWidget: 切换到注册界面"; - emit showRegister(); -} - -void LoginWidget::clearInputs() -{ - qDebug() << "LoginWidget: 清空输入框"; - usernameEdit->clear(); - passwordEdit->clear(); -} diff --git a/src/frontend/loginwidget.h b/src/frontend/loginwidget.h deleted file mode 100644 index 87fe657..0000000 --- a/src/frontend/loginwidget.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef LOGINWIDGET_H -#define LOGINWIDGET_H - -#include -#include -#include -#include -#include -#include -#include "usermanage.h" - -class LoginWidget : public QWidget -{ - Q_OBJECT - -public: - explicit LoginWidget(QWidget *parent = nullptr); - -signals: - void loginSuccess(User* user); - void showRegister(); - -private slots: - void onLoginClicked(); - void onRegisterClicked(); - -public slots: - void clearInputs(); - -private: - QLineEdit *usernameEdit; // 改为用户名输入 - QLineEdit *passwordEdit; - QPushButton *loginButton; - QPushButton *registerButton; - UserManager userManager; -}; - -#endif diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp deleted file mode 100644 index e1366fd..0000000 --- a/src/frontend/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include "mainwindow.h" - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - - qDebug() << "应用程序启动..."; - - // 设置应用程序信息 - app.setApplicationName("数学学习软件"); - app.setApplicationVersion("1.0"); - app.setOrganizationName("软件工程学院"); - - qDebug() << "创建主窗口..."; - MainWindow window; - - qDebug() << "显示主窗口..."; - window.show(); - - qDebug() << "进入事件循环..."; - return app.exec(); -} diff --git a/src/frontend/mainmenuwidget.cpp b/src/frontend/mainmenuwidget.cpp deleted file mode 100644 index ae6f2a1..0000000 --- a/src/frontend/mainmenuwidget.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "mainmenuwidget.h" -#include -#include - -MainMenuWidget::MainMenuWidget(QWidget *parent) : QWidget(parent) -{ - QVBoxLayout *mainLayout = new QVBoxLayout(this); - - // 标题和欢迎信息 - QLabel *titleLabel = new QLabel("数学学习软件"); - titleLabel->setAlignment(Qt::AlignCenter); - titleLabel->setStyleSheet("font-size: 24px; font-weight: bold; margin: 30px; color: #2c3e50;"); - - welcomeLabel = new QLabel("欢迎使用数学学习软件"); - welcomeLabel->setAlignment(Qt::AlignCenter); - welcomeLabel->setStyleSheet("font-size: 18px; margin: 20px; color: #34495e;"); - - userInfoLabel = new QLabel(); - userInfoLabel->setAlignment(Qt::AlignCenter); - userInfoLabel->setStyleSheet("font-size: 14px; margin: 10px; color: #7f8c8d;"); - - // 设置区域 - QGroupBox *settingsGroup = new QGroupBox("考试设置"); - settingsGroup->setStyleSheet("QGroupBox {" - "font-size: 16px;" - "font-weight: bold;" - "margin-top: 10px;" - "}" - "QGroupBox::title {" - "subcontrol-origin: margin;" - "subcontrol-position: top center;" - "padding: 0 5px;" - "}"); - - QVBoxLayout *settingsLayout = new QVBoxLayout(settingsGroup); - - // 年级选择 - QWidget *gradeWidget = new QWidget(); - QHBoxLayout *gradeLayout = new QHBoxLayout(gradeWidget); - QLabel *gradeLabel = new QLabel("选择年级:"); - gradeLabel->setStyleSheet("font-size: 14px;"); - gradeComboBox = new QComboBox(); - gradeComboBox->addItem("小学"); - gradeComboBox->addItem("初中"); - gradeComboBox->addItem("高中"); - gradeComboBox->setStyleSheet("padding: 8px; font-size: 14px;"); - gradeLayout->addWidget(gradeLabel); - gradeLayout->addWidget(gradeComboBox); - gradeLayout->addStretch(); - - // 题目数量 - QWidget *countWidget = new QWidget(); - QHBoxLayout *countLayout = new QHBoxLayout(countWidget); - QLabel *countLabel = new QLabel("题目数量:"); - countLabel->setStyleSheet("font-size: 14px;"); - questionCountSpinBox = new QSpinBox(); - questionCountSpinBox->setRange(5, 30); - questionCountSpinBox->setValue(10); - questionCountSpinBox->setStyleSheet("padding: 8px; font-size: 14px;"); - countLayout->addWidget(countLabel); - countLayout->addWidget(questionCountSpinBox); - countLayout->addStretch(); - - // 开始按钮 - startButton = new QPushButton("开始考试"); - startButton->setStyleSheet("QPushButton {" - "background-color: #e74c3c;" - "color: white;" - "border: none;" - "padding: 12px 30px;" - "font-size: 16px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #c0392b;" - "}"); - startButton->setFixedSize(150, 50); - - // 退出登录按钮 - logoutButton = new QPushButton("退出登录"); - logoutButton->setStyleSheet("QPushButton {" - "background-color: #95a5a6;" - "color: white;" - "border: none;" - "padding: 12px 30px;" - "font-size: 16px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #7f8c8d;" - "}"); - logoutButton->setFixedSize(150, 50); // 修改为相同大小 - - // 按钮容器 - 重新设计布局 - QWidget *buttonWidget = new QWidget(); - QVBoxLayout *buttonLayout = new QVBoxLayout(buttonWidget); - buttonLayout->setSpacing(15); // 设置按钮间距 - buttonLayout->setContentsMargins(0, 20, 0, 10); - - // 开始考试按钮行 - QWidget *startButtonWidget = new QWidget(); - QHBoxLayout *startButtonLayout = new QHBoxLayout(startButtonWidget); - startButtonLayout->addStretch(); - startButtonLayout->addWidget(startButton); - startButtonLayout->addStretch(); - - // 退出登录按钮行 - QWidget *logoutButtonWidget = new QWidget(); - QHBoxLayout *logoutButtonLayout = new QHBoxLayout(logoutButtonWidget); - logoutButtonLayout->addStretch(); - logoutButtonLayout->addWidget(logoutButton); - logoutButtonLayout->addStretch(); - - // 添加到按钮布局 - buttonLayout->addWidget(startButtonWidget); - buttonLayout->addWidget(logoutButtonWidget); - - // 添加到设置布局 - settingsLayout->addWidget(gradeWidget); - settingsLayout->addWidget(countWidget); - settingsLayout->addSpacing(20); - settingsLayout->addWidget(buttonWidget); - - // 添加到主布局 - mainLayout->addWidget(titleLabel); - mainLayout->addWidget(welcomeLabel); - mainLayout->addWidget(userInfoLabel); - mainLayout->addSpacing(20); - mainLayout->addWidget(settingsGroup); - mainLayout->addStretch(); - - // 连接信号槽 - connect(startButton, &QPushButton::clicked, this, &MainMenuWidget::onStartExamClicked); - connect(logoutButton, &QPushButton::clicked, this, &MainMenuWidget::onLogoutClicked); - - qDebug() << "MainMenuWidget: 初始化完成,退出登录按钮大小已调整"; -} - -void MainMenuWidget::setUserInfo(const std::wstring& username, const std::wstring& grade) -{ - QString userInfo = QString("当前用户: %1 | 注册年级: %2") - .arg(QString::fromStdWString(username)) - .arg(QString::fromStdWString(grade)); - userInfoLabel->setText(userInfo); - - // 设置年级选择框默认值 - QString gradeStr = QString::fromStdWString(grade); - int index = gradeComboBox->findText(gradeStr); - if (index >= 0) { - gradeComboBox->setCurrentIndex(index); - } -} - -QString MainMenuWidget::getSelectedGrade() const { - return gradeComboBox->currentText(); -} - -void MainMenuWidget::onStartExamClicked() -{ - int questionCount = questionCountSpinBox->value(); - QString selectedGrade = gradeComboBox->currentText(); - qDebug() << "MainMenuWidget: 开始考试,题目数量:" << questionCount << "选择年级:" << selectedGrade; - emit startExam(questionCount); -} - -void MainMenuWidget::onLogoutClicked() -{ - qDebug() << "MainMenuWidget: 用户请求退出登录"; - - // 弹出确认对话框 - QMessageBox::StandardButton reply; - reply = QMessageBox::question(this, "确认退出", - "确定要退出登录吗?", - QMessageBox::Yes | QMessageBox::No); - - if (reply == QMessageBox::Yes) { - qDebug() << "MainMenuWidget: 用户确认退出登录"; - emit logoutRequested(); - } else { - qDebug() << "MainMenuWidget: 用户取消退出登录"; - } -} diff --git a/src/frontend/mainmenuwidget.h b/src/frontend/mainmenuwidget.h deleted file mode 100644 index f3c56d4..0000000 --- a/src/frontend/mainmenuwidget.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef MAINMENUWIDGET_H -#define MAINMENUWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include - -class MainMenuWidget : public QWidget -{ - Q_OBJECT - -public: - explicit MainMenuWidget(QWidget *parent = nullptr); - void setUserInfo(const std::wstring& username, const std::wstring& grade); - QString getSelectedGrade() const; - -signals: - void startExam(int questionCount); - void logoutRequested(); // 退出登录 - -private slots: - void onStartExamClicked(); - void onLogoutClicked(); // 处理退出登录 - -private: - QLabel *welcomeLabel; - QLabel *userInfoLabel; - QComboBox *gradeComboBox; - QSpinBox *questionCountSpinBox; - QPushButton *startButton; - QPushButton *logoutButton; // 退出登录按钮 -}; - -#endif diff --git a/src/frontend/mainwindow.cpp b/src/frontend/mainwindow.cpp deleted file mode 100644 index f3064ad..0000000 --- a/src/frontend/mainwindow.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include "mainwindow.h" -#include -#include -#include - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), currentUser(nullptr) -{ - setWindowTitle("中小学数学学习软件"); - setMinimumSize(600, 700); - resize(600, 700); - - qDebug() << "MainWindow: 初始化开始"; - - // 创建堆叠窗口 - stackedWidget = new QStackedWidget(this); - setCentralWidget(stackedWidget); - - // 创建各个界面 - loginWidget = new LoginWidget(); - registerWidget = new RegisterWidget(); - mainMenuWidget = new MainMenuWidget(); - examWidget = new ExamWidget(); - resultWidget = new ResultWidget(); - - qDebug() << "MainWindow: 所有界面创建完成"; - - // 添加到堆叠窗口 - stackedWidget->addWidget(loginWidget); - stackedWidget->addWidget(registerWidget); - stackedWidget->addWidget(mainMenuWidget); - stackedWidget->addWidget(examWidget); - stackedWidget->addWidget(resultWidget); - - // 连接信号槽 - connect(loginWidget, &LoginWidget::loginSuccess, this, &MainWindow::onUserLoggedIn); - connect(loginWidget, &LoginWidget::showRegister, this, &MainWindow::showRegister); - - connect(registerWidget, &RegisterWidget::showLogin, this, &MainWindow::showLogin); - connect(registerWidget, &RegisterWidget::registerSuccess, this, &MainWindow::onRegisterSuccess); - - connect(mainMenuWidget, &MainMenuWidget::startExam, this, &MainWindow::showExam); - connect(mainMenuWidget, &MainMenuWidget::logoutRequested, this, &MainWindow::onLogoutRequested); // 新增连接 - - connect(examWidget, &ExamWidget::examFinished, this, &MainWindow::showResult); - connect(resultWidget, &ResultWidget::backToMenu, this, &MainWindow::showMainMenu); - connect(resultWidget, &ResultWidget::startNewExam, this, &MainWindow::showMainMenu); - - qDebug() << "MainWindow: 信号连接完成"; - - // 显示登录界面 - showLogin(); -} - -MainWindow::~MainWindow() -{ - qDebug() << "MainWindow: 析构函数调用"; -} - -void MainWindow::showLogin() -{ - qDebug() << "MainWindow: 切换到登录界面"; - stackedWidget->setCurrentWidget(loginWidget); - setWindowTitle("中小学数学学习软件 - 登录"); -} - -void MainWindow::showRegister() -{ - qDebug() << "MainWindow: 切换到注册界面"; - stackedWidget->setCurrentWidget(registerWidget); - setWindowTitle("中小学数学学习软件 - 注册"); -} - -void MainWindow::showMainMenu() -{ - qDebug() << "MainWindow: 切换到主菜单"; - if (currentUser) { - mainMenuWidget->setUserInfo(currentUser->getUsername(), - currentUser->getGradeString()); - stackedWidget->setCurrentWidget(mainMenuWidget); - setWindowTitle("中小学数学学习软件 - 主菜单"); - } else { - qDebug() << "MainWindow: 当前用户为空,跳转到登录界面"; - QMessageBox::warning(this, "会话过期", "用户会话已过期,请重新登录"); - showLogin(); - } -} - -void MainWindow::showExam(int questionCount) -{ - qDebug() << "MainWindow: 开始考试,题目数量:" << questionCount; - if (currentUser) { - // 从主菜单获取当前选择的年级,而不是用户注册时的年级 - QString selectedGrade = mainMenuWidget->getSelectedGrade(); - qDebug() << "MainWindow: 用户注册年级:" << QString::fromStdWString(currentUser->getGradeString()) - << "选择的年级:" << selectedGrade; - - // 根据选择的年级设置考试难度 - Grade examGrade = Grade::PRIMARY; // 默认小学 - - if (selectedGrade == "小学") { - examGrade = Grade::PRIMARY; - } else if (selectedGrade == "初中") { - examGrade = Grade::JUNIOR; - } else if (selectedGrade == "高中") { - examGrade = Grade::SENIOR; - } - - qDebug() << "MainWindow: 实际考试年级:" << static_cast(examGrade); - examWidget->startExam(examGrade, questionCount); - stackedWidget->setCurrentWidget(examWidget); - setWindowTitle("中小学数学学习软件 - 考试中"); - } else { - qDebug() << "MainWindow: 考试时用户未登录"; - QMessageBox::warning(this, "错误", "用户未登录,请重新登录"); - showLogin(); - } -} - -void MainWindow::showResult(int score, int total) -{ - qDebug() << "MainWindow: 显示考试结果,分数:" << score << "/" << total; - resultWidget->setResult(score, total); - stackedWidget->setCurrentWidget(resultWidget); - setWindowTitle("中小学数学学习软件 - 考试结果"); - - // 保存考试记录 - if (currentUser) { - bool saved = FileSaver::saveExamRecord(currentUser->getUsername(), - currentUser->getGradeString(), - score, total, - FileSaver::getCurrentTime()); - if (saved) { - qDebug() << "MainWindow: 考试记录保存成功"; - } else { - qDebug() << "MainWindow: 考试记录保存失败"; - } - } -} - -void MainWindow::onUserLoggedIn(User* user) -{ - qDebug() << "MainWindow: 用户登录成功"; - if (user) { - currentUser = user; - QString username = QString::fromStdWString(user->getUsername()); - QString grade = QString::fromStdWString(user->getGradeString()); - - qDebug() << "MainWindow: 登录用户:" << username << "年级:" << grade; - - QMessageBox::information(this, "登录成功", - QString("欢迎 %1!\n年级:%2") - .arg(username) - .arg(grade)); - showMainMenu(); - } else { - qDebug() << "MainWindow: 用户信息无效"; - QMessageBox::warning(this, "登录失败", "用户信息无效"); - showLogin(); - } -} - -void MainWindow::onRegisterSuccess() -{ - qDebug() << "MainWindow: 注册成功,准备跳转到登录界面"; - - // 注册成功后显示登录界面,并显示成功提示 - QMessageBox::information(this, "注册成功", "注册成功!请使用新账号登录"); - - // 清空登录界面的输入框 - if (loginWidget) { - loginWidget->clearInputs(); - } - - qDebug() << "MainWindow: 清空登录界面输入框完成"; - - // 立即切换到登录界面 - showLogin(); - - qDebug() << "MainWindow: 已切换到登录界面"; -} - -void MainWindow::onLogoutRequested() -{ - qDebug() << "MainWindow: 处理退出登录请求"; - - // 清除当前用户信息 - if (currentUser) { - QString username = QString::fromStdWString(currentUser->getUsername()); - qDebug() << "MainWindow: 用户" << username << "退出登录"; - currentUser = nullptr; - } - - // 清空登录界面的输入框 - if (loginWidget) { - loginWidget->clearInputs(); - } - - // 显示登录界面 - showLogin(); - - QMessageBox::information(this, "退出成功", "您已成功退出登录"); -} diff --git a/src/frontend/mainwindow.h b/src/frontend/mainwindow.h deleted file mode 100644 index 7135fcc..0000000 --- a/src/frontend/mainwindow.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include "loginwidget.h" -#include "registerwidget.h" -#include "mainmenuwidget.h" -#include "examwidget.h" -#include "resultwidget.h" -#include "usermanage.h" -#include "filesaver.h" - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - -private slots: - void showLogin(); - void showRegister(); - void showMainMenu(); - void showExam(int questionCount); - void showResult(int score, int total); - void onUserLoggedIn(User* user); - void onRegisterSuccess(); - void onLogoutRequested(); // 处理退出登录 - -private: - QStackedWidget *stackedWidget; - LoginWidget *loginWidget; - RegisterWidget *registerWidget; - MainMenuWidget *mainMenuWidget; - ExamWidget *examWidget; - ResultWidget *resultWidget; - UserManager userManager; - User *currentUser; -}; - -#endif diff --git a/src/frontend/mainwindow.ui b/src/frontend/mainwindow.ui deleted file mode 100644 index b232854..0000000 --- a/src/frontend/mainwindow.ui +++ /dev/null @@ -1,22 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 800 - 600 - - - - MainWindow - - - - - - - - diff --git a/src/frontend/mathlearningApp.pro b/src/frontend/mathlearningApp.pro deleted file mode 100644 index dd44b16..0000000 --- a/src/frontend/mathlearningApp.pro +++ /dev/null @@ -1,49 +0,0 @@ -QT += core gui network - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -CONFIG += c++11 - -# The following define makes your compiler emit warnings if you use -# any Qt feature that has been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -SOURCES += \ - filesaver.cpp \ - questiongenerator.cpp \ - user.cpp \ - usermanage.cpp \ - loginwidget.cpp \ - main.cpp \ - mainmenuwidget.cpp \ - mainwindow.cpp \ - registerwidget.cpp \ - resultwidget.cpp \ - examwidget.cpp - -HEADERS += \ - filesaver.h \ - questiongenerator.h \ - user.h \ - usermanage.h \ - loginwidget.h \ - mainmenuwidget.h \ - mainwindow.h \ - registerwidget.h \ - resultwidget.h \ - examwidget.h - -FORMS += \ - mainwindow.ui - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target diff --git a/src/frontend/registerwidget.cpp b/src/frontend/registerwidget.cpp deleted file mode 100644 index 1e20d93..0000000 --- a/src/frontend/registerwidget.cpp +++ /dev/null @@ -1,391 +0,0 @@ -#include "registerwidget.h" -#include "usermanage.h" -#include -#include -#include -#include -#include -#include - -RegisterWidget::RegisterWidget(QWidget *parent) : QWidget(parent), countdownSeconds(0) -{ - // 设置最小尺寸 - setMinimumSize(500, 600); - - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setSpacing(20); - mainLayout->setContentsMargins(40, 30, 40, 30); - - // 标题 - QLabel *titleLabel = new QLabel("用户注册"); - titleLabel->setAlignment(Qt::AlignCenter); - titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; margin: 20px; color: #2c3e50;"); - - // 创建网格布局用于表单 - QGridLayout *formLayout = new QGridLayout(); - formLayout->setSpacing(15); - formLayout->setColumnMinimumWidth(0, 100); - formLayout->setColumnStretch(1, 1); - - // 邮箱输入 - QLabel *emailLabel = new QLabel("邮箱:"); - emailLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - emailEdit = new QLineEdit(); - emailEdit->setPlaceholderText("请输入您的邮箱"); - emailEdit->setMinimumHeight(35); - emailEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 发送验证码按钮 - sendCodeButton = new QPushButton("发送验证码"); - sendCodeButton->setFixedWidth(120); - sendCodeButton->setMinimumHeight(35); - sendCodeButton->setStyleSheet("QPushButton {" - "background-color: #3498db;" - "color: white;" - "border: none;" - "font-size: 12px;" - "border-radius: 4px;" - "}" - "QPushButton:hover {" - "background-color: #2980b9;" - "}" - "QPushButton:disabled {" - "background-color: #bdc3c7;" - "}"); - - // 验证码输入 - QLabel *codeLabel = new QLabel("验证码:"); - codeLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - verificationCodeEdit = new QLineEdit(); - verificationCodeEdit->setPlaceholderText("请输入收到的验证码"); - verificationCodeEdit->setMinimumHeight(35); - verificationCodeEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 计时器标签 - timerLabel = new QLabel(); - timerLabel->setStyleSheet("color: #e74c3c; font-size: 14px; font-weight: bold;"); - timerLabel->setFixedWidth(60); - timerLabel->setAlignment(Qt::AlignCenter); - - // 用户名输入 - QLabel *usernameLabel = new QLabel("用户名:"); - usernameLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - usernameEdit = new QLineEdit(); - usernameEdit->setPlaceholderText("请输入用户名(2-20个字符,建议使用英文)"); - usernameEdit->setMinimumHeight(35); - usernameEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 年级选择 - QLabel *gradeLabel = new QLabel("选择年级:"); - gradeLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - gradeComboBox = new QComboBox(); - gradeComboBox->addItem("小学"); - gradeComboBox->addItem("初中"); - gradeComboBox->addItem("高中"); - gradeComboBox->setMinimumHeight(35); - gradeComboBox->setStyleSheet("QComboBox { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 密码输入 - QLabel *passwordLabel = new QLabel("密码:"); - passwordLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - passwordEdit = new QLineEdit(); - passwordEdit->setEchoMode(QLineEdit::Password); - passwordEdit->setPlaceholderText("6-10位,包含大小写字母和数字"); - passwordEdit->setMinimumHeight(35); - passwordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 确认密码 - QLabel *confirmPasswordLabel = new QLabel("确认密码:"); - confirmPasswordLabel->setStyleSheet("font-size: 14px; font-weight: bold;"); - confirmPasswordEdit = new QLineEdit(); - confirmPasswordEdit->setEchoMode(QLineEdit::Password); - confirmPasswordEdit->setPlaceholderText("请再次输入密码"); - confirmPasswordEdit->setMinimumHeight(35); - confirmPasswordEdit->setStyleSheet("QLineEdit { padding: 8px; font-size: 14px; border: 1px solid #bdc3c7; border-radius: 4px; }"); - - // 设置表单布局 - int row = 0; - - // 邮箱行 - formLayout->addWidget(emailLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); - QHBoxLayout *emailLayout = new QHBoxLayout(); - emailLayout->setSpacing(10); - emailLayout->addWidget(emailEdit); - emailLayout->addWidget(sendCodeButton); - formLayout->addLayout(emailLayout, row, 1); - row++; - - // 验证码行 - formLayout->addWidget(codeLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); - QHBoxLayout *codeLayout = new QHBoxLayout(); - codeLayout->setSpacing(10); - codeLayout->addWidget(verificationCodeEdit); - codeLayout->addWidget(timerLabel); - formLayout->addLayout(codeLayout, row, 1); - row++; - - // 用户名行 - formLayout->addWidget(usernameLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); - formLayout->addWidget(usernameEdit, row, 1); - row++; - - // 年级行 - formLayout->addWidget(gradeLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); - formLayout->addWidget(gradeComboBox, row, 1); - row++; - - // 密码行 - formLayout->addWidget(passwordLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); - formLayout->addWidget(passwordEdit, row, 1); - row++; - - // 确认密码行 - formLayout->addWidget(confirmPasswordLabel, row, 0, Qt::AlignRight | Qt::AlignVCenter); - formLayout->addWidget(confirmPasswordEdit, row, 1); - row++; - - // 按钮区域 - QWidget *buttonWidget = new QWidget(); - QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); - buttonLayout->setSpacing(30); - buttonLayout->setContentsMargins(0, 30, 0, 0); - - registerButton = new QPushButton("注册"); - registerButton->setFixedSize(140, 45); - registerButton->setStyleSheet("QPushButton {" - "background-color: #27ae60;" - "color: white;" - "border: none;" - "font-size: 16px;" - "font-weight: bold;" - "border-radius: 6px;" - "}" - "QPushButton:hover {" - "background-color: #229954;" - "}"); - - backButton = new QPushButton("返回登录"); - backButton->setFixedSize(140, 45); - backButton->setStyleSheet("QPushButton {" - "background-color: #95a5a6;" - "color: white;" - "border: none;" - "font-size: 16px;" - "font-weight: bold;" - "border-radius: 6px;" - "}" - "QPushButton:hover {" - "background-color: #7f8c8d;" - "}"); - - buttonLayout->addStretch(); - buttonLayout->addWidget(registerButton); - buttonLayout->addWidget(backButton); - buttonLayout->addStretch(); - - // 添加到主布局 - mainLayout->addWidget(titleLabel); - mainLayout->addLayout(formLayout); - mainLayout->addStretch(); - mainLayout->addWidget(buttonWidget); - - // 初始化计时器 - countdownTimer = new QTimer(this); - countdownTimer->setInterval(1000); - - // 连接信号槽 - connect(sendCodeButton, &QPushButton::clicked, this, &RegisterWidget::onSendCodeClicked); - connect(registerButton, &QPushButton::clicked, this, &RegisterWidget::onRegisterClicked); - connect(backButton, &QPushButton::clicked, this, &RegisterWidget::onBackClicked); - connect(countdownTimer, &QTimer::timeout, this, &RegisterWidget::updateTimer); - - // 初始状态 - resetCountdown(); - - qDebug() << "RegisterWidget: 初始化完成"; -} - -void RegisterWidget::onSendCodeClicked() -{ - QString email = emailEdit->text().trimmed(); - qDebug() << "RegisterWidget: 发送验证码到:" << email; - - if (!validateEmail(email)) { - QMessageBox::warning(this, "输入错误", "请输入有效的邮箱地址"); - return; - } - - // 使用UserManager发送验证码 - std::wstring emailW = email.toStdWString(); - std::wstring generatedCodeW; - - if (userManager.sendVerificationCode(emailW, generatedCodeW)) { - generatedCode = QString::fromStdWString(generatedCodeW); - - QMessageBox::information(this, "验证码已发送", - QString("验证码已发送到 %1\n验证码:%2\n(验证码10分钟内有效)") - .arg(email).arg(QString::fromStdWString(generatedCodeW))); - - // 开始倒计时 - startCountdown(); - } else { - QMessageBox::warning(this, "发送失败", "验证码发送失败,请重试"); - } -} - -void RegisterWidget::onRegisterClicked() -{ - QString email = emailEdit->text().trimmed(); - QString username = usernameEdit->text().trimmed(); - QString verificationCode = verificationCodeEdit->text().trimmed(); - QString password = passwordEdit->text(); - QString confirmPassword = confirmPasswordEdit->text(); - QString grade = gradeComboBox->currentText(); - - qDebug() << "RegisterWidget: 开始注册,用户名:" << username << "年级:" << grade; - - // 验证输入 - if (email.isEmpty() || username.isEmpty() || verificationCode.isEmpty() || - password.isEmpty() || confirmPassword.isEmpty()) { - QMessageBox::warning(this, "输入错误", "请填写所有字段"); - return; - } - - if (!validateEmail(email)) { - QMessageBox::warning(this, "输入错误", "请输入有效的邮箱地址"); - return; - } - - // 验证验证码 - 使用UserManager验证 - std::wstring emailW = email.toStdWString(); - std::wstring codeW = verificationCode.toStdWString(); - - if (!userManager.verifyEmailCode(emailW, codeW)) { - QMessageBox::warning(this, "验证错误", "验证码错误或已过期"); - return; - } - - // 验证密码 - if (password != confirmPassword) { - QMessageBox::warning(this, "输入错误", "两次输入的密码不一致"); - return; - } - - if (!validatePassword(password)) { - QMessageBox::warning(this, "输入错误", - "密码必须为6-10位,且包含大小写字母和数字"); - return; - } - - if (username.length() < 2 || username.length() > 20) { - QMessageBox::warning(this, "输入错误", "用户名长度应在2-20个字符之间"); - return; - } - - // 实际注册用户 - 使用安全的用户名避免编码问题 - std::wstring safeUsername; - for (QChar ch : username) { - if (ch.isLetterOrNumber() || ch == '_') { - safeUsername += ch.unicode(); - } - } - - // 如果用户名不合法,生成一个随机用户名 - if (safeUsername.empty()) { - safeUsername = L"user" + std::to_wstring(1000 + (std::rand() % 9000)); - qDebug() << "RegisterWidget: 生成随机用户名:" << QString::fromStdWString(safeUsername); - } - - qDebug() << "RegisterWidget: 尝试注册用户:" << QString::fromStdWString(safeUsername); - - if (userManager.registerUser(safeUsername, - password.toStdWString(), - grade.toStdWString())) { - qDebug() << "RegisterWidget: 注册成功"; - QMessageBox::information(this, "注册成功", - QString("注册成功!\n用户名: %1\n请使用新账号登录") - .arg(QString::fromStdWString(safeUsername))); - - // 重置表单 - emailEdit->clear(); - usernameEdit->clear(); - verificationCodeEdit->clear(); - passwordEdit->clear(); - confirmPasswordEdit->clear(); - resetCountdown(); - - qDebug() << "RegisterWidget: 发射注册成功信号"; - - // 发射信号 - emit registerSuccess(); - emit showLogin(); - - qDebug() << "RegisterWidget: 信号发射完成"; - - } else { - qDebug() << "RegisterWidget: 注册失败"; - QMessageBox::warning(this, "注册失败", "用户名已存在,请选择其他用户名"); - } -} - -void RegisterWidget::onBackClicked() -{ - qDebug() << "RegisterWidget: 返回登录"; - resetCountdown(); - emit showLogin(); -} - -bool RegisterWidget::validatePassword(const QString &password) -{ - if (password.length() < 6 || password.length() > 10) { - return false; - } - - bool hasUpper = false, hasLower = false, hasDigit = false; - for (QChar ch : password) { - if (ch.isUpper()) hasUpper = true; - else if (ch.isLower()) hasLower = true; - else if (ch.isDigit()) hasDigit = true; - } - - return hasUpper && hasLower && hasDigit; -} - -bool RegisterWidget::validateEmail(const QString &email) -{ - QRegularExpression emailRegex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"); - return emailRegex.match(email).hasMatch(); -} - -void RegisterWidget::startCountdown() -{ - countdownSeconds = 600; // 10分钟 - sendCodeButton->setEnabled(false); - updateTimer(); - countdownTimer->start(); - qDebug() << "RegisterWidget: 开始倒计时,10分钟"; -} - -void RegisterWidget::resetCountdown() -{ - countdownTimer->stop(); - countdownSeconds = 0; - sendCodeButton->setEnabled(true); - timerLabel->clear(); - generatedCode.clear(); - qDebug() << "RegisterWidget: 重置倒计时"; -} - -void RegisterWidget::updateTimer() -{ - if (countdownSeconds > 0) { - countdownSeconds--; - int minutes = countdownSeconds / 60; - int seconds = countdownSeconds % 60; - timerLabel->setText(QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')) - .arg(seconds, 2, 10, QLatin1Char('0'))); - } else { - resetCountdown(); - QMessageBox::information(this, "提示", "验证码已过期,请重新获取"); - } -} diff --git a/src/frontend/registerwidget.h b/src/frontend/registerwidget.h deleted file mode 100644 index ed5084d..0000000 --- a/src/frontend/registerwidget.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef REGISTERWIDGET_H -#define REGISTERWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "usermanage.h" - -class RegisterWidget : public QWidget -{ - Q_OBJECT - -public: - explicit RegisterWidget(QWidget *parent = nullptr); - -signals: - void registerSuccess(); - void showLogin(); - -private slots: - void onSendCodeClicked(); - void onRegisterClicked(); - void onBackClicked(); - void updateTimer(); - -private: - QLineEdit *emailEdit; - QLineEdit *usernameEdit; - QLineEdit *verificationCodeEdit; - QLineEdit *passwordEdit; - QLineEdit *confirmPasswordEdit; - QComboBox *gradeComboBox; - QPushButton *sendCodeButton; - QPushButton *registerButton; - QPushButton *backButton; - QLabel *timerLabel; - - QTimer *countdownTimer; - int countdownSeconds; - QString generatedCode; - - UserManager userManager; // 添加用户管理器 - - bool validatePassword(const QString &password); - bool validateEmail(const QString &email); - void startCountdown(); - void resetCountdown(); -}; - -#endif diff --git a/src/frontend/resultwidget.cpp b/src/frontend/resultwidget.cpp deleted file mode 100644 index 23f9f3e..0000000 --- a/src/frontend/resultwidget.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "resultwidget.h" - -ResultWidget::ResultWidget(QWidget *parent) : QWidget(parent) -{ - QVBoxLayout *mainLayout = new QVBoxLayout(this); - - // 标题 - QLabel *titleLabel = new QLabel("考试结果"); - titleLabel->setAlignment(Qt::AlignCenter); - titleLabel->setStyleSheet("font-size: 24px; font-weight: bold; margin: 30px; color: #2c3e50;"); - - // 结果区域 - QWidget *resultWidget = new QWidget(); - QVBoxLayout *resultLayout = new QVBoxLayout(resultWidget); - resultLayout->setContentsMargins(50, 30, 50, 30); - - resultLabel = new QLabel("考试完成!"); - resultLabel->setAlignment(Qt::AlignCenter); - resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #34495e;"); - - scoreLabel = new QLabel(); - scoreLabel->setAlignment(Qt::AlignCenter); - scoreLabel->setStyleSheet("font-size: 36px; font-weight: bold; margin: 20px; color: #e74c3c;"); - - percentageLabel = new QLabel(); - percentageLabel->setAlignment(Qt::AlignCenter); - percentageLabel->setStyleSheet("font-size: 18px; margin: 10px; color: #7f8c8d;"); - - // 按钮区域 - QWidget *buttonWidget = new QWidget(); - QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); - - backButton = new QPushButton("返回主菜单"); - backButton->setStyleSheet("QPushButton {" - "background-color: #95a5a6;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #7f8c8d;" - "}"); - backButton->setFixedWidth(120); - - newExamButton = new QPushButton("继续做题"); - newExamButton->setStyleSheet("QPushButton {" - "background-color: #3498db;" - "color: white;" - "border: none;" - "padding: 10px 20px;" - "font-size: 14px;" - "border-radius: 5px;" - "}" - "QPushButton:hover {" - "background-color: #2980b9;" - "}"); - newExamButton->setFixedWidth(120); - - buttonLayout->addStretch(); - buttonLayout->addWidget(backButton); - buttonLayout->addSpacing(20); - buttonLayout->addWidget(newExamButton); - buttonLayout->addStretch(); - - // 添加到结果布局 - resultLayout->addWidget(resultLabel); - resultLayout->addWidget(scoreLabel); - resultLayout->addWidget(percentageLabel); - resultLayout->addSpacing(30); - resultLayout->addWidget(buttonWidget); - - // 添加到主布局 - mainLayout->addWidget(titleLabel); - mainLayout->addWidget(resultWidget); - mainLayout->addStretch(); - - // 连接信号槽 - connect(backButton, &QPushButton::clicked, this, &ResultWidget::onBackClicked); - connect(newExamButton, &QPushButton::clicked, this, &ResultWidget::onNewExamClicked); -} - -void ResultWidget::setResult(int score, int total) -{ - double percentage = (double)score / total * 100; - - scoreLabel->setText(QString("%1 / %2").arg(score).arg(total)); - percentageLabel->setText(QString("正确率: %1%").arg(percentage, 0, 'f', 1)); - - // 根据分数显示不同的评价 - if (percentage >= 90) { - resultLabel->setText("优秀!你的表现非常出色!"); - resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #27ae60;"); - } else if (percentage >= 70) { - resultLabel->setText("良好!继续努力!"); - resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #f39c12;"); - } else if (percentage >= 60) { - resultLabel->setText("及格!还有提升空间!"); - resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #e67e22;"); - } else { - resultLabel->setText("需要加油!再多练习一下吧!"); - resultLabel->setStyleSheet("font-size: 20px; margin: 20px; color: #e74c3c;"); - } -} - -void ResultWidget::onBackClicked() -{ - emit backToMenu(); -} - -void ResultWidget::onNewExamClicked() -{ - emit startNewExam(); -} diff --git a/src/frontend/resultwidget.h b/src/frontend/resultwidget.h deleted file mode 100644 index 0343e7e..0000000 --- a/src/frontend/resultwidget.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef RESULTWIDGET_H -#define RESULTWIDGET_H - -#include -#include -#include -#include -#include - -class ResultWidget : public QWidget -{ - Q_OBJECT - -public: - explicit ResultWidget(QWidget *parent = nullptr); - void setResult(int score, int total); - -signals: - void backToMenu(); - void startNewExam(); - -private slots: - void onBackClicked(); - void onNewExamClicked(); - -private: - QLabel *resultLabel; - QLabel *scoreLabel; - QLabel *percentageLabel; - QPushButton *backButton; - QPushButton *newExamButton; -}; - -#endif