Compare commits

..

No commits in common. 'main' and 'wuronghai_branch' have entirely different histories.

@ -1,87 +0,0 @@
# 学习软件项目文档
## 项目概述
该学习软件是一个基于Java Swing的应用程序提供数学题目练习和考试功能。项目采用MVC架构包含用户管理、题目生成、考试系统等核心模块用户注册账号登录后选择难度进行数学考试软件会给出考试的正确率和得分率。
## 运行环境
编码环境是Java的1.8.0版本运行jar包没有问题打包成exe文件后在Windows系统x64下可以直接运行功能正常
## 使用流程
初始界面可以选择登录或者注册选择注册输入用户名和邮箱根据收到的注册码确认密码完成注册。输入用户名和密码登录进入主功能界面初始小学难度可切换题目数限制在10-30按开始考试进入答题。下方有修改密码和退出登录选项退出登录会回到初始界面。
## 目录结构
src/
├── controller/ 控制器层
├── model/ 数据模型层
├── view/ 视图层
├── utils/ 工具类
└── MathLearningApp.java 应用入口
## 核心功能模块
### 1. 用户管理模块
用户注册:邮箱验证码注册机制
用户登录:用户名密码登录
密码管理:修改密码功能
用户状态:注册状态管理
### 2. 题目管理模块
题目生成:根据难度自动生成数学题目
难度分级:小学、初中、高中三个难度级别
题目类型:四则运算、平方平方根、三角函数等
选项生成:自动生成干扰选项
### 3. 考试系统模块
试卷生成:按难度和题目数量生成试卷
答题界面:友好的用户交互界面
进度管理:题目导航和进度显示
成绩统计:自动评分和正确率计算
### 4. 界面导航模块
视图管理:统一的界面切换控制
用户状态保持:登录用户信息管理
## 核心类说明
### 控制器层 (Controller)
BaseController: 抽象基类,提供验证、日志、消息提示等通用功能
UserController: 用户业务逻辑实现UserService接口
QuestionController: 题目业务逻辑实现QuestionService接口
NavigationController: 界面导航控制实现NavigationService接口
### 服务接口 (Service Interfaces)
UserService: 用户相关业务方法契约
QuestionService: 题目相关业务方法契约
NavigationService: 界面导航方法契约
### 数据模型 (Model)
User: 用户数据模型
Question: 题目数据模型
QuestionGenerator: 题目生成器
### 视图层 (View)
LoginView: 用户登录界面
RegisterView: 用户注册界面
MainView: 主功能界面
ExamView: 考试答题界面
ResultView: 成绩显示界面
ChangePasswordDialog: 修改密码对话框
### 工具类 (Utils)
EmailUtil: 邮件发送功能
FileUtil: 文件读写操作
ValidationUtil: 数据验证工具

Binary file not shown.

@ -1,111 +0,0 @@
package utils;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
import java.util.Random;
public class EmailUtil {
private static final Random random = new Random();
// SMTP服务器配置 - 请修改为您的邮箱配置
private static final String SMTP_HOST = "smtp.qq.com"; // QQ邮箱SMTP
private static final String SMTP_PORT = "587"; // 端口
private static final String FROM_EMAIL = "2536082954@qq.com"; // 发件邮箱
private static final String EMAIL_PASSWORD = "uihuasdsosbvdiia"; // 授权码
private static final String FROM_NAME = "数学学习软件";
public static String generateRegistrationCode() {
return String.format("%06d", random.nextInt(1000000));
}
/**
*
*/
public static boolean sendRegistrationCode(String userEmail, String username, String code) {
// 配置邮件服务器
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", SMTP_HOST);
props.put("mail.smtp.port", SMTP_PORT);
// 超时设置
props.put("mail.smtp.timeout", "10000");
props.put("mail.smtp.connectiontimeout", "10000");
// 创建认证器
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(FROM_EMAIL, EMAIL_PASSWORD);
}
};
Session session = Session.getInstance(props, authenticator);
try {
// 创建邮件
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(FROM_EMAIL, FROM_NAME, "UTF-8"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(userEmail));
message.setSubject("数学学习软件 - 注册验证码", "UTF-8");
// 邮件内容HTML格式
String htmlContent = buildEmailContent(username, code);
message.setContent(htmlContent, "text/html;charset=UTF-8");
// 发送邮件
Transport.send(message);
System.out.println("✅ 注册码已成功发送到: " + userEmail);
return true;
} catch (Exception e) {
System.err.println("❌ 邮件发送失败: " + e.getMessage());
e.printStackTrace();
return false;
}
}
/**
*
*/
private static String buildEmailContent(String username, String code) {
return "<!DOCTYPE html>" +
"<html>" +
"<head>" +
" <meta charset=\"UTF-8\">" +
" <style>" +
" body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }" +
" .container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }" +
" .header { color: #1890ff; text-align: center; margin-bottom: 30px; }" +
" .code-box { background: #f0f8ff; padding: 20px; text-align: center; margin: 25px 0; border-radius: 8px; border: 2px dashed #1890ff; }" +
" .code { font-size: 32px; font-weight: bold; color: #ff4d4f; letter-spacing: 5px; }" +
" .footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #666; font-size: 12px; }" +
" </style>" +
"</head>" +
"<body>" +
" <div class=\"container\">" +
" <div class=\"header\">" +
" <h1>数学学习软件 - 注册验证码</h1>" +
" </div>" +
" <p>亲爱的 <strong>" + username + "</strong>,您好!</p>" +
" <p>您正在注册数学学习软件,请使用以下验证码完成注册:</p>" +
" <div class=\"code-box\">" +
" <div class=\"code\">" + code + "</div>" +
" </div>" +
" <p>如果这不是您的操作,请忽略此邮件。</p>" +
" <div class=\"footer\">" +
" <p>此为系统邮件,请勿回复</p>" +
" <p>数学学习软件团队</p>" +
" </div>" +
" </div>" +
"</body>" +
"</html>";
}
}

@ -1,200 +0,0 @@
package utils;
import model.Question;
import model.User;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class FileUtil {
private static final String USERS_FILE = "data/users.json";
private static final String CURRENT_EXAM_FILE = "data/current_exam.json";
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
static {
new File("data").mkdirs();
}
/**
*
*/
public static void cleanupUnregisteredUsers() {
try {
Map<String, User> users = loadAllUsers();
boolean hasChanges = false;
long currentTime = System.currentTimeMillis();
// 使用迭代器安全删除
Iterator<Map.Entry<String, User>> iterator = users.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, User> entry = iterator.next();
User user = entry.getValue();
// 删除条件:未设置密码 或 未完成注册 且 创建时间超过24小时
if ((user.getPassword() == null || user.getPassword().isEmpty() || !user.isRegistered())) {
iterator.remove();
hasChanges = true;
System.out.println("清理未注册用户: " + user.getUsername() + " (" + user.getEmail() + ")");
}
}
if (hasChanges) {
saveAllUsers(users);
} else {
System.out.println("没有需要清理的未注册用户");
}
} catch (Exception e) {
System.err.println("清理未注册用户时出错: " + e.getMessage());
e.printStackTrace();
}
}
public static void saveAllUsers(Map<String, User> users) {
try (FileWriter writer = new FileWriter(USERS_FILE)) {
gson.toJson(users, writer);
} catch (IOException e) {
System.err.println("保存用户数据失败: " + e.getMessage());
}
}
// 用户相关方法保持不变...
public static void saveUser(User user) {
try {
Map<String, User> users = loadAllUsers();
users.put(user.getUsername(), user);
saveAllUsers(users);
} catch (Exception e) {
System.err.println("保存用户数据失败: " + e.getMessage());
}
}
public static Map<String, User> loadAllUsers() {
try {
File file = new File(USERS_FILE);
if (!file.exists()) {
return new HashMap<>();
}
try (FileReader reader = new FileReader(file)) {
Type type = new TypeToken<Map<String, User>>(){}.getType();
Map<String, User> users = gson.fromJson(reader, type);
// 修复现有数据中的问题
if (users != null) {
for (Map.Entry<String, User> entry : users.entrySet()) {
User user = entry.getValue();
// 确保用户名不为空
if (user.getUsername() == null) {
user.setUsername(entry.getKey());
}
// 确保密码不为null
if (user.getPassword() == null) {
user.setPassword("");
}
}
}
return users != null ? users : new HashMap<>();
}
} catch (IOException e) {
System.err.println("加载用户数据失败: " + e.getMessage());
return new HashMap<>();
}
}
public static User loadUserByUsername(String username) {
Map<String, User> users = loadAllUsers();
return users.get(username);
}
public static User loadUserByEmail(String email) {
Map<String, User> users = loadAllUsers();
for (User user : users.values()) {
if (user.getEmail().equals(email)) {
return user;
}
}
return null;
}
public static boolean usernameExists(String username) {
return loadAllUsers().containsKey(username);
}
public static boolean emailExists(String email) {
return loadUserByEmail(email) != null;
}
// ========== 试卷保存相关方法 ==========
/**
*
*/
public static void saveCurrentExam(List<Question> questions, String difficulty, int questionCount) {
try {
ExamData examData = new ExamData(questions, difficulty, questionCount, new Date());
try (FileWriter writer = new FileWriter(CURRENT_EXAM_FILE)) {
gson.toJson(examData, writer);
}
System.out.println("✅ 试卷已保存到本地: " + CURRENT_EXAM_FILE);
} catch (IOException e) {
System.err.println("保存试卷失败: " + e.getMessage());
}
}
/**
*
*/
public static ExamData loadCurrentExam() {
try {
File file = new File(CURRENT_EXAM_FILE);
if (!file.exists()) {
return null;
}
try (FileReader reader = new FileReader(file)) {
return gson.fromJson(reader, ExamData.class);
}
} catch (IOException e) {
System.err.println("加载试卷失败: " + e.getMessage());
return null;
}
}
/**
*
*/
public static class ExamData {
private List<Question> questions;
private String difficulty;
private int questionCount;
private Date generateTime;
public ExamData(List<Question> questions, String difficulty, int questionCount, Date generateTime) {
this.questions = questions;
this.difficulty = difficulty;
this.questionCount = questionCount;
this.generateTime = generateTime;
}
// Getters
public List<Question> getQuestions() { return questions; }
public String getDifficulty() { return difficulty; }
public int getQuestionCount() { return questionCount; }
public Date getGenerateTime() { return generateTime; }
}
}

@ -1,39 +0,0 @@
package utils;
import java.util.regex.Pattern;
public class ValidationUtil {
public static boolean isValidEmail(String email) {
String emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$";
return Pattern.compile(emailRegex).matcher(email).matches();
}
public static boolean isValidPassword(String password) {
// 6-10位必须包含大小写字母和数字
if (password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpperCase = false;
boolean hasLowerCase = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) hasUpperCase = true;
if (Character.isLowerCase(c)) hasLowerCase = true;
if (Character.isDigit(c)) hasDigit = true;
}
return hasUpperCase && hasLowerCase && hasDigit;
}
public static boolean isNumeric(String str) {
try {
Double.parseDouble(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}

@ -1,131 +0,0 @@
package view;
import controller.UserController;
import model.User;
import utils.ValidationUtil;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Font;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ChangePasswordDialog extends JDialog {
private User currentUser;
private UserController userController;
private JPasswordField oldPasswordField;
private JPasswordField newPasswordField;
private JPasswordField confirmPasswordField;
public ChangePasswordDialog(JFrame parent, User user) {
super(parent, "修改密码", true);
this.currentUser = user;
this.userController = new UserController();
initializeUI();
}
private void initializeUI() {
setSize(400, 300);
setLocationRelativeTo(getParent());
setResizable(false);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Title
JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// Form panel
JPanel formPanel = new JPanel(new GridLayout(4, 2, 10, 10));
JLabel oldPasswordLabel = new JLabel("原密码:");
oldPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
oldPasswordField = new JPasswordField();
JLabel newPasswordLabel = new JLabel("新密码:");
newPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
newPasswordField = new JPasswordField();
JLabel confirmPasswordLabel = new JLabel("确认新密码:");
confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
confirmPasswordField = new JPasswordField();
JButton confirmButton = new JButton("确认修改");
confirmButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JButton cancelButton = new JButton("取消");
cancelButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(oldPasswordLabel);
formPanel.add(oldPasswordField);
formPanel.add(newPasswordLabel);
formPanel.add(newPasswordField);
formPanel.add(confirmPasswordLabel);
formPanel.add(confirmPasswordField);
formPanel.add(confirmButton);
formPanel.add(cancelButton);
mainPanel.add(formPanel, BorderLayout.CENTER);
// 密码要求提示
JLabel hintLabel = new JLabel(
"<html><body style='text-align: center'>" +
"密码要求6-10位必须包含大小写字母和数字<br>" +
"例如Abc123、Test456" +
"</body></html>", JLabel.CENTER);
hintLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
hintLabel.setForeground(Color.GRAY);
mainPanel.add(hintLabel, BorderLayout.SOUTH);
// Add action listeners
confirmButton.addActionListener(new ConfirmAction());
cancelButton.addActionListener(e -> dispose());
// Enter key support
oldPasswordField.addActionListener(new ConfirmAction());
newPasswordField.addActionListener(new ConfirmAction());
confirmPasswordField.addActionListener(new ConfirmAction());
add(mainPanel);
}
private class ConfirmAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String oldPassword = new String(oldPasswordField.getPassword());
String newPassword = new String(newPasswordField.getPassword());
String confirmPassword = new String(confirmPasswordField.getPassword());
// 验证输入
if (oldPassword.isEmpty() || newPassword.isEmpty() || confirmPassword.isEmpty()) {
JOptionPane.showMessageDialog(ChangePasswordDialog.this,
"请填写所有密码字段", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
// 调用控制器修改密码
String result = userController.changePassword(currentUser, oldPassword, newPassword, confirmPassword);
if (result.equals("密码修改成功")) {
JOptionPane.showMessageDialog(ChangePasswordDialog.this,
"密码修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
dispose(); // 关闭对话框
} else {
JOptionPane.showMessageDialog(ChangePasswordDialog.this,
result, "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
}

@ -1,281 +0,0 @@
package view;
import controller.NavigationController;
import controller.NavigationService;
import controller.QuestionController;
import model.Question;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JButton;
import javax.swing.ButtonGroup;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class ExamView extends JFrame {
private QuestionController questionController;
private NavigationService navigationService;
private JLabel questionLabel;
private JLabel progressLabel;
private ButtonGroup optionGroup;
private JRadioButton[] optionButtons;
private JButton prevButton;
private JButton nextButton;
private JButton submitButton;
private JLabel hintLabel;
public ExamView() {
questionController = new QuestionController();
navigationService = NavigationController.getInstance();
initializeUI();
}
private void initializeUI() {
setTitle("数学学习软件 - 答题界面");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(600, 450);
setLocationRelativeTo(null);
setResizable(false);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Progress label
progressLabel = new JLabel("", JLabel.CENTER);
progressLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
mainPanel.add(progressLabel, BorderLayout.NORTH);
// Question panel
JPanel questionPanel = new JPanel(new BorderLayout(10, 10));
questionLabel = new JLabel("", JLabel.CENTER);
questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18));
questionPanel.add(questionLabel, BorderLayout.NORTH);
// Options panel
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10));
optionGroup = new ButtonGroup();
optionButtons = new JRadioButton[4];
for (int i = 0; i < 4; i++) {
optionButtons[i] = new JRadioButton();
optionButtons[i].setFont(new Font("微软雅黑", Font.PLAIN, 14));
optionGroup.add(optionButtons[i]);
optionsPanel.add(optionButtons[i]);
optionButtons[i].addItemListener(new OptionSelectionListener());
}
questionPanel.add(optionsPanel, BorderLayout.CENTER);
mainPanel.add(questionPanel, BorderLayout.CENTER);
// Hint label
hintLabel = new JLabel("请选择答案后继续", JLabel.CENTER);
hintLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
hintLabel.setForeground(Color.RED);
hintLabel.setVisible(false);
mainPanel.add(hintLabel, BorderLayout.SOUTH);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
prevButton = new JButton("上一题");
nextButton = new JButton("下一题");
submitButton = new JButton("提交试卷");
prevButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
nextButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
submitButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
nextButton.setEnabled(false);
buttonPanel.add(prevButton);
buttonPanel.add(nextButton);
buttonPanel.add(submitButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// Add action listeners
prevButton.addActionListener(new PrevButtonAction());
nextButton.addActionListener(new NextButtonAction());
submitButton.addActionListener(new SubmitButtonAction());
add(mainPanel);
}
public void startNewExam(String difficulty, int questionCount) {
questionController.startNewExam(difficulty, questionCount);
displayCurrentQuestion();
updateNavigationButtons();
updateNextButtonState();
}
private void displayCurrentQuestion() {
Question question = questionController.getCurrentQuestion();
if (question == null) return;
progressLabel.setText(String.format("第 %d 题 / 共 %d 题",
questionController.getCurrentQuestionNumber(),
questionController.getTotalQuestions()));
questionLabel.setText(question.getQuestion());
String[] options = question.getOptions();
for (int i = 0; i < 4; i++) {
optionButtons[i].setText((char)('A' + i) + ". " + options[i]);
}
// Restore user's previous selection
int userAnswer = questionController.getUserAnswerForCurrentQuestion();
optionGroup.clearSelection();
if (userAnswer != -1) {
optionButtons[userAnswer].setSelected(true);
nextButton.setEnabled(canGoNext());
hintLabel.setVisible(false);
} else {
nextButton.setEnabled(false);
}
}
private void updateNavigationButtons() {
int current = questionController.getCurrentQuestionNumber();
int total = questionController.getTotalQuestions();
prevButton.setEnabled(current > 1);
// 修改这里:最后一题时禁用下一题按钮
boolean isLastQuestion = current == total;
nextButton.setEnabled(!isLastQuestion && isCurrentQuestionAnswered());
submitButton.setEnabled(isLastQuestion && isCurrentQuestionAnswered());
}
private void updateNextButtonState() {
boolean isLastQuestion = questionController.getCurrentQuestionNumber() == questionController.getTotalQuestions();
if (isLastQuestion) {
// 最后一题:禁用下一题,启用提交按钮(如果已回答)
nextButton.setEnabled(false);
submitButton.setEnabled(isCurrentQuestionAnswered());
} else {
// 不是最后一题:根据是否回答控制下一题按钮
nextButton.setEnabled(isCurrentQuestionAnswered());
submitButton.setEnabled(false);
}
hintLabel.setVisible(!isCurrentQuestionAnswered());
}
private boolean canGoNext() {
int current = questionController.getCurrentQuestionNumber();
int total = questionController.getTotalQuestions();
return current < total && isCurrentQuestionAnswered();
}
private boolean isCurrentQuestionAnswered() {
for (int i = 0; i < 4; i++) {
if (optionButtons[i].isSelected()) {
return true;
}
}
return false;
}
private void saveCurrentAnswer() {
for (int i = 0; i < 4; i++) {
if (optionButtons[i].isSelected()) {
questionController.submitAnswer(i);
break;
}
}
}
// 选项选择监听器
private class OptionSelectionListener implements ItemListener {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
saveCurrentAnswer(); // 立即保存答案
updateNextButtonState();
updateNavigationButtons();
}
}
}
private class PrevButtonAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
saveCurrentAnswer();
if (questionController.previousQuestion()) {
displayCurrentQuestion();
updateNavigationButtons();
updateNextButtonState();
}
}
}
private class NextButtonAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int current = questionController.getCurrentQuestionNumber();
int total = questionController.getTotalQuestions();
// 检查是否是最后一题
if (current >= total) {
JOptionPane.showMessageDialog(ExamView.this,
"已经是最后一题了,请点击提交试卷!", "提示",
JOptionPane.INFORMATION_MESSAGE);
return;
}
if (!isCurrentQuestionAnswered()) {
JOptionPane.showMessageDialog(ExamView.this,
"请先选择答案!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
saveCurrentAnswer();
if (questionController.nextQuestion()) {
displayCurrentQuestion();
updateNavigationButtons();
updateNextButtonState();
} else {
// 如果无法切换到下一题,可能是最后一题
JOptionPane.showMessageDialog(ExamView.this,
"无法切换到下一题,请点击提交试卷!", "提示",
JOptionPane.INFORMATION_MESSAGE);
}
}
}
private class SubmitButtonAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (!isCurrentQuestionAnswered()) {
JOptionPane.showMessageDialog(ExamView.this,
"请先完成本题!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
saveCurrentAnswer();
int score = questionController.calculateScore();
int total = questionController.getTotalQuestions();
double percentage = questionController.getPercentage();
// 关闭当前窗口
dispose();
// 显示结果页面
navigationService.showResultView(score, total, percentage); // 修改
}
}
}

@ -1,130 +0,0 @@
package view;
import controller.NavigationController;
import controller.NavigationService;
import controller.UserController;
import controller.UserService;
import model.User;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LoginView extends JFrame {
private JTextField usernameField;
private JPasswordField passwordField;
private UserService userController;
private NavigationService navigationService;
public LoginView() {
userController = new UserController();
navigationService = NavigationController.getInstance();
initializeUI();
cleanupUnregisteredUsers();
}
private void cleanupUnregisteredUsers() {
try {
userController.cleanupUnregisteredUsers();
} catch (Exception e) {
System.err.println("清理未注册用户时出错: " + e.getMessage());
}
}
private void initializeUI() {
setTitle("数学学习软件 - 登录");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationRelativeTo(null);
setResizable(false);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Title
JLabel titleLabel = new JLabel("用户登录", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// Form panel
JPanel formPanel = new JPanel(new GridLayout(3, 2, 10, 10));
JLabel usernameLabel = new JLabel("用户名:");
usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
usernameField = new JTextField();
JLabel passwordLabel = new JLabel("密码:");
passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
passwordField = new JPasswordField();
formPanel.add(usernameLabel);
formPanel.add(usernameField);
formPanel.add(passwordLabel);
formPanel.add(passwordField);
// Buttons
JPanel buttonPanel = new JPanel(new FlowLayout());
JButton loginButton = new JButton("登录");
JButton registerButton = new JButton("注册");
loginButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
buttonPanel.add(loginButton);
buttonPanel.add(registerButton);
formPanel.add(new JLabel()); // Empty cell
formPanel.add(buttonPanel);
mainPanel.add(formPanel, BorderLayout.CENTER);
// Add action listeners
loginButton.addActionListener(new LoginAction());
registerButton.addActionListener(e -> {
navigationService.showRegisterView();
});
// Enter key support
usernameField.addActionListener(new LoginAction());
passwordField.addActionListener(new LoginAction());
add(mainPanel);
}
private class LoginAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String username = usernameField.getText().trim();
String password = new String(passwordField.getPassword());
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(LoginView.this,
"请输入用户名和密码", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
User user = userController.login(username, password);
if (user != null) {
JOptionPane.showMessageDialog(LoginView.this,
"登录成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
navigationService.showMainView(user);
} else {
JOptionPane.showMessageDialog(LoginView.this,
"用户名或密码错误", "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
}

@ -1,126 +0,0 @@
package view;
import controller.NavigationController;
import controller.NavigationService;
import controller.UserController;
import model.User;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainView extends JFrame {
private JComboBox<String> difficultyComboBox;
private JTextField countField;
private User currentUser; // 保存当前登录用户
private NavigationService navigationService;
public MainView(User user) {
this.currentUser = user;
this.navigationService = NavigationController.getInstance();
initializeUI();
}
private void initializeUI() {
setTitle("数学学习软件 - 主界面");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(450, 350);
setLocationRelativeTo(null);
setResizable(false);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Title - 显示欢迎信息
JLabel titleLabel = new JLabel("欢迎 " + currentUser.getUsername() + "", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// Form panel
JPanel formPanel = new JPanel(new GridLayout(3, 2, 10, 10));
JLabel difficultyLabel = new JLabel("难度级别:");
difficultyLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
String[] difficulties = {"小学", "初中", "高中"};
difficultyComboBox = new JComboBox<>(difficulties);
difficultyComboBox.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JLabel countLabel = new JLabel("题目数量:");
countLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
countField = new JTextField("10");
countField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JButton startButton = new JButton("开始答题");
startButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
formPanel.add(difficultyLabel);
formPanel.add(difficultyComboBox);
formPanel.add(countLabel);
formPanel.add(countField);
formPanel.add(new JLabel()); // Empty cell
formPanel.add(startButton);
mainPanel.add(formPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout());
JButton changePasswordButton = new JButton("修改密码");
JButton logoutButton = new JButton("退出登录");
changePasswordButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
logoutButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
buttonPanel.add(changePasswordButton);
buttonPanel.add(logoutButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// Add action listeners
startButton.addActionListener(new StartExamAction());
changePasswordButton.addActionListener(new ChangePasswordAction());
logoutButton.addActionListener(e -> navigationService.showLoginView()); // 修改
add(mainPanel);
}
private class StartExamAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String difficulty = (String) difficultyComboBox.getSelectedItem();
String countText = countField.getText().trim();
try {
int count = Integer.parseInt(countText);
if (count < 10 || count > 30) {
JOptionPane.showMessageDialog(MainView.this,
"题目数量必须在10-30之间", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
navigationService.showExamView(difficulty, count); // 修改
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(MainView.this,
"请输入有效的数字", "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
private class ChangePasswordAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// 打开修改密码对话框
new ChangePasswordDialog(MainView.this, currentUser).setVisible(true);
}
}
}

@ -1,164 +0,0 @@
package view;
import controller.NavigationController;
import controller.NavigationService;
import controller.UserController;
import model.User;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class RegisterView extends JFrame {
private JTextField usernameField; // 用户名输入框
private JTextField emailField;
private JTextField codeField;
private JPasswordField passwordField;
private JPasswordField confirmPasswordField;
private UserController userController;
private NavigationService navigationService; // 新增
private String currentUsername; // 保存当前注册的用户名
public RegisterView() {
userController = new UserController();
navigationService = NavigationController.getInstance(); // 新增
initializeUI();
}
private void initializeUI() {
setTitle("数学学习软件 - 注册");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(550, 500);
setLocationRelativeTo(null);
setResizable(false);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Title
JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// Form panel - 增加用户名字段
JPanel formPanel = new JPanel(new GridLayout(8, 2, 10, 10));
JLabel usernameLabel = new JLabel("用户名:");
usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
usernameField = new JTextField();
JLabel emailLabel = new JLabel("邮箱:");
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
emailField = new JTextField();
JLabel codeLabel = new JLabel("注册码:");
codeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
codeField = new JTextField();
JLabel passwordLabel = new JLabel("密码:");
passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
passwordField = new JPasswordField();
JLabel confirmPasswordLabel = new JLabel("确认密码:");
confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
confirmPasswordField = new JPasswordField();
JButton sendCodeButton = new JButton("发送注册码");
sendCodeButton.setFont(new Font("微软雅黑", Font.PLAIN, 12));
JButton registerButton = new JButton("完成注册");
registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
// 布局表单
formPanel.add(usernameLabel);
formPanel.add(usernameField);
formPanel.add(emailLabel);
formPanel.add(emailField);
formPanel.add(sendCodeButton);
formPanel.add(new JLabel());
formPanel.add(codeLabel);
formPanel.add(codeField);
formPanel.add(passwordLabel);
formPanel.add(passwordField);
formPanel.add(confirmPasswordLabel);
formPanel.add(confirmPasswordField);
formPanel.add(registerButton);
mainPanel.add(formPanel, BorderLayout.CENTER);
// Back button
JButton backButton = new JButton("返回登录");
backButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
backButton.addActionListener(e -> navigationService.showLoginView()); // 修改
JPanel bottomPanel = new JPanel(new FlowLayout());
bottomPanel.add(backButton);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
// Add action listeners
sendCodeButton.addActionListener(new SendCodeAction());
registerButton.addActionListener(new RegisterAction());
add(mainPanel);
}
private class SendCodeAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String username = usernameField.getText().trim();
String email = emailField.getText().trim();
if (username.isEmpty() || email.isEmpty()) {
JOptionPane.showMessageDialog(RegisterView.this,
"请输入用户名和邮箱", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
String result = userController.registerUser(username, email);
JOptionPane.showMessageDialog(RegisterView.this, result);
if (result.contains("发送")) {
currentUsername = username;
}
}
}
private class RegisterAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String username = usernameField.getText().trim();
String code = codeField.getText().trim();
String password = new String(passwordField.getPassword());
String confirmPassword = new String(confirmPasswordField.getPassword());
if (username.isEmpty() || code.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) {
JOptionPane.showMessageDialog(RegisterView.this,
"请填写所有字段", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
String result = userController.completeRegistration(username, code, password, confirmPassword);
if (result.equals("注册成功!")) {
JOptionPane.showMessageDialog(RegisterView.this,
result, "成功", JOptionPane.INFORMATION_MESSAGE);
navigationService.showLoginView(); // 修改
} else {
JOptionPane.showMessageDialog(RegisterView.this,
result, "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
}

@ -1,79 +0,0 @@
package view;
import controller.NavigationController;
import controller.NavigationService;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ResultView extends JFrame {
private JLabel scoreLabel;
private JLabel percentageLabel;
private NavigationService navigationService;
public ResultView() {
navigationService = NavigationController.getInstance(); // 新增
initializeUI();
}
private void initializeUI() {
setTitle("数学学习软件 - 成绩界面");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationRelativeTo(null);
setResizable(false);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// Title
JLabel titleLabel = new JLabel("考试结果", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
mainPanel.add(titleLabel, BorderLayout.NORTH);
// Result panel
JPanel resultPanel = new JPanel(new GridLayout(2, 1, 10, 10));
scoreLabel = new JLabel("", JLabel.CENTER);
scoreLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18));
percentageLabel = new JLabel("", JLabel.CENTER);
percentageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18));
resultPanel.add(scoreLabel);
resultPanel.add(percentageLabel);
mainPanel.add(resultPanel, BorderLayout.CENTER);
// Button panel
JPanel buttonPanel = new JPanel(new FlowLayout());
JButton continueButton = new JButton("继续做题");
JButton exitButton = new JButton("退出系统");
continueButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
exitButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
buttonPanel.add(continueButton);
buttonPanel.add(exitButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// Add action listeners
continueButton.addActionListener(e -> navigationService.showMainView()); // 修改
exitButton.addActionListener(e -> System.exit(0));
add(mainPanel);
}
public void setResults(int score, int total, double percentage) {
scoreLabel.setText(String.format("得分: %d / %d", score, total));
percentageLabel.setText(String.format("正确率: %.1f%%", percentage));
}
}
Loading…
Cancel
Save