代码最终版 #8

Merged
hnu202326010112 merged 11 commits from develop into main 3 months ago

@ -0,0 +1,113 @@
# 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。

@ -0,0 +1,205 @@
#include "filesaver.h"
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <sstream>
//创建目录(跨平台实现)
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<char>(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<char>(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<char>(wc);
}
}
return result;
}
//将题目列表写入文件的实现
bool FileSaver::writeQuestionsToFile(const std::string& filename,
const std::string& safeUsername,
const std::string& englishGrade,
const std::vector<std::wstring>& 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<char>(wc);
break;
default: file << static_cast<char>(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<std::wstring>& 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;
}

@ -0,0 +1,63 @@
#ifndef FILESAVER_H
#define FILESAVER_H
#include <string>
#include <vector>
#include <chrono>
#include <sstream>
#include <iomanip>
#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif
// 文件保存器类,负责将生成的数学题目保存到文件系统
class FileSaver {
public:
//将题目列表写入指定文件
static bool writeQuestionsToFile(const std::string& filename,
const std::string& safeUsername,
const std::string& englishGrade,
const std::vector<std::wstring>& 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<std::wstring>& 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

@ -0,0 +1,596 @@
#include "questiongenerator.h"
#include <random>
#include <sstream>
#include <cmath>
#include <string>
#include <algorithm>
#include <iomanip>
#include <QDebug>
#include <functional>
//题目生成器构造函数的实现
QuestionGenerator::QuestionGenerator() : currentGrade(Grade::PRIMARY), gen(rd()) {}
//设置年级的实现
void QuestionGenerator::setGrade(Grade grade) {
currentGrade = grade;
qDebug() << "QuestionGenerator: 设置年级为:" << static_cast<int>(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<std::wstring> operators = {L"+", L"-", L"×", L"÷"};
std::uniform_int_distribution<> dis(0, static_cast<int>(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<std::wstring> numbers;
std::vector<std::wstring> 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<std::wstring> numbers;
std::vector<std::wstring> 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<int>(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<int>(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<std::wstring> 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<std::wstring> numbers;
std::vector<std::wstring> 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<int>(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<int>(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<std::wstring> QuestionGenerator::generateQuestions(int count) {
// 验证题目数量范围
if (count < 10 || count > 30) {
qDebug() << "QuestionGenerator: 题目数量超出范围(10-30)使用默认值10";
count = 10;
}
std::vector<std::wstring> 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<int>(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<double(const std::wstring&)> 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<int>(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<int>(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<std::wstring> 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<std::wstring> QuestionGenerator::generateMeaningfulOptions(double correctAnswer) {
try {
std::vector<std::wstring> options;
std::uniform_real_distribution<double> dis(-2.0, 2.0);
std::set<double> 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<int>(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();
}

@ -0,0 +1,78 @@
#ifndef QUESTIONGENERATOR_H
#define QUESTIONGENERATOR_H
#include "user.h"
#include <string>
#include <vector>
#include <set>
#include <random>
#include <cmath>
#include <sstream>
#include <iomanip>
//题目生成器类,根据年级生成不同难度的数学题目
//支持小学、初中、高中三个级别的题目生成,包含查重功能。
class QuestionGenerator {
private:
Grade currentGrade;
std::set<std::wstring> 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<std::wstring> generateQuestions(int count);
//生成题目和选项用于GUI
struct QuestionWithOptions {
std::wstring question;
std::vector<std::wstring> options;
int correctIndex;
};
QuestionWithOptions generateQuestionWithOptions();
//清空已生成题目的历史记录
void clearHistory();
// 计算正确答案
double calculateCorrectAnswer(const std::wstring& question);
// 生成有意义的选项
std::vector<std::wstring> generateMeaningfulOptions(double correctAnswer);
};
#endif

@ -0,0 +1,41 @@
#include "user.h"
#include <string>
//用户构造函数的实现
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;
}

@ -0,0 +1,41 @@
#ifndef USER_H
#define USER_H
#include <string>
//年级枚举,表示用户所属的学段级别
//用于区分不同学段的题目难度和生成规则
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

@ -0,0 +1,431 @@
#include "usermanage.h"
#include <iostream>
#include <algorithm>
#include <sstream>
#include <codecvt>
#include <locale>
#include <chrono>
#include <random>
#include <QDebug>
#include <fstream>
#include <ctime>
#include <QProcess> // 添加进程支持
#include <QDir> // 添加目录支持
// 初始化用户数据的实现
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<char>(wc);
for (wchar_t wc : password) password_str += static_cast<char>(wc);
for (wchar_t wc : gradeStr) grade_str += static_cast<char>(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<unsigned int>(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;
}
}

@ -0,0 +1,75 @@
#ifndef USERMANAGE_H
#define USERMANAGE_H
#include "user.h"
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <map>
#include <random>
#include <chrono>
#include <QProcess> // 添加进程支持
// 邮箱验证码结构
struct EmailVerification {
std::wstring code;
std::chrono::system_clock::time_point expiryTime;
bool used;
};
// 用户管理器类,负责用户数据的初始化和认证管理
class UserManager {
private:
std::vector<User> users;
const std::string userFile = "users.dat";
// 邮箱验证码存储
std::map<std::wstring, EmailVerification> 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<User>& getUsers() { return users; }
};
#endif

@ -0,0 +1,376 @@
#include "examwidget.h"
#include <QMessageBox>
#include <random>
#include <QGroupBox>
#include <QTimer>
#include <QDebug>
#include <sstream>
#include <iomanip>
#include <cmath>
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<int>(grade) << "题目数量:" << questionCount;
questionGenerator.setGrade(grade);
questions = questionGenerator.generateQuestions(questionCount);
totalQuestions = static_cast<int>(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<std::wstring> 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<int>(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<int>(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<size_t>(index) < questionOptions.size()) {
for (int i = 0; i < 4; ++i) {
if (i < static_cast<int>(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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<int>(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);
}

@ -0,0 +1,56 @@
#ifndef EXAMWIDGET_H
#define EXAMWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QRadioButton>
#include <QButtonGroup>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <vector>
#include <sstream>
#include <iomanip>
#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<std::wstring> questions;
std::vector<int> userAnswers;
std::vector<int> correctAnswers;
std::vector<std::vector<std::wstring>> questionOptions;
int currentQuestion;
int totalQuestions;
};
#endif

@ -0,0 +1,153 @@
#include "loginwidget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QDebug>
#include <QApplication>
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();
}

@ -0,0 +1,38 @@
#ifndef LOGINWIDGET_H
#define LOGINWIDGET_H
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QMessageBox>
#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

@ -0,0 +1,24 @@
#include <QApplication>
#include <QDebug>
#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();
}

@ -0,0 +1,182 @@
#include "mainmenuwidget.h"
#include <QMessageBox>
#include <QDebug>
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: 用户取消退出登录";
}
}

@ -0,0 +1,39 @@
#ifndef MAINMENUWIDGET_H
#define MAINMENUWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QSpinBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
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

@ -0,0 +1,203 @@
#include "mainwindow.h"
#include <QMessageBox>
#include <QTimer>
#include <QDebug>
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<int>(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, "退出成功", "您已成功退出登录");
}

@ -0,0 +1,43 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStackedWidget>
#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

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar"/>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

@ -0,0 +1,49 @@
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

@ -0,0 +1,391 @@
#include "registerwidget.h"
#include "usermanage.h"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QRandomGenerator>
#include <QTime>
#include <QTimer>
#include <QDebug>
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, "提示", "验证码已过期,请重新获取");
}
}

@ -0,0 +1,59 @@
#ifndef REGISTERWIDGET_H
#define REGISTERWIDGET_H
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QRegularExpression>
#include <QComboBox>
#include <QTimer>
#include <QRandomGenerator>
#include <QHBoxLayout>
#include <QGridLayout>
#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

@ -0,0 +1,115 @@
#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();
}

@ -0,0 +1,34 @@
#ifndef RESULTWIDGET_H
#define RESULTWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
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
Loading…
Cancel
Save