Compare commits
9 Commits
main
...
Violet_bra
| Author | SHA1 | Date |
|---|---|---|
|
|
74bae27d89 | 5 months ago |
|
|
260ec5d55d | 5 months ago |
|
|
a762ca5ed0 | 5 months ago |
|
|
262525a2f6 | 5 months ago |
|
|
3e2e8ddb6c | 5 months ago |
|
|
30cec4d97d | 5 months ago |
|
|
8f80b21da7 | 5 months ago |
|
|
b19b40ca1d | 5 months ago |
|
|
822f33e7de | 5 months ago |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,70 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>math-learning-app</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- JavaMail API -->
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JAF (Java Activation Framework) -->
|
||||
<dependency>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>ui.MathLearningApp</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,22 @@
|
||||
package model;
|
||||
|
||||
/**
|
||||
* 年级类型枚举
|
||||
* 定义小学、初中、高中三个年级
|
||||
*/
|
||||
public enum GradeType {
|
||||
/**
|
||||
* 小学
|
||||
*/
|
||||
PRIMARY,
|
||||
|
||||
/**
|
||||
* 初中
|
||||
*/
|
||||
MIDDLE,
|
||||
|
||||
/**
|
||||
* 高中
|
||||
*/
|
||||
HIGH
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package model;
|
||||
|
||||
/**
|
||||
* 题目类
|
||||
* 包含题干、选项和正确答案
|
||||
*/
|
||||
public class Question {
|
||||
private final String questionText;
|
||||
private final String[] options;
|
||||
private final int correctAnswer;
|
||||
private final String expression;
|
||||
|
||||
/**
|
||||
* 构造题目对象
|
||||
*
|
||||
* @param expression 数学表达式
|
||||
* @param questionText 题目文本
|
||||
* @param options 选项数组(4个选项)
|
||||
* @param correctAnswer 正确答案索引(0-3)
|
||||
*/
|
||||
public Question(String expression, String questionText, String[] options,
|
||||
int correctAnswer) {
|
||||
this.expression = expression;
|
||||
this.questionText = questionText;
|
||||
this.options = options;
|
||||
this.correctAnswer = correctAnswer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取题目文本
|
||||
*
|
||||
* @return 题目文本
|
||||
*/
|
||||
public String getQuestionText() {
|
||||
return questionText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选项数组
|
||||
*
|
||||
* @return 选项数组
|
||||
*/
|
||||
public String[] getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正确答案索引
|
||||
*
|
||||
* @return 正确答案索引
|
||||
*/
|
||||
public int getCorrectAnswer() {
|
||||
return correctAnswer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数学表达式
|
||||
*
|
||||
* @return 数学表达式
|
||||
*/
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
package ui;
|
||||
|
||||
import model.UserManager;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 修改密码对话框
|
||||
* 用户在登录状态下可以修改密码
|
||||
*/
|
||||
public class ChangePasswordDialog extends JDialog {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Pattern PASSWORD_PATTERN =
|
||||
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z0-9]{6,10}$");
|
||||
|
||||
private final JPasswordField oldPasswordField;
|
||||
private final JPasswordField newPassword1Field;
|
||||
private final JPasswordField newPassword2Field;
|
||||
private final JButton confirmButton;
|
||||
private final UserManager userManager;
|
||||
private final String email;
|
||||
|
||||
/**
|
||||
* 构造修改密码对话框
|
||||
*
|
||||
* @param parent 父窗口
|
||||
* @param email 用户邮箱
|
||||
*/
|
||||
public ChangePasswordDialog(Frame parent, String email) {
|
||||
super(parent, "修改密码", true);
|
||||
this.email = email;
|
||||
this.userManager = new UserManager();
|
||||
|
||||
setSize(600, 360);
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(true);
|
||||
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel infoLabel = new JLabel("<html>新密码要求:<br>" +
|
||||
"1. 长度 6-10位<br>" +
|
||||
"2. 必须包含大写字母<br>" +
|
||||
"3. 必须包含小写字母<br>" +
|
||||
"4. 必须包含数字<br>" +
|
||||
"5. 不能包含特殊字符</html>");
|
||||
infoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 11));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
panel.add(infoLabel, gbc);
|
||||
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridy = 1;
|
||||
panel.add(new JLabel("原密码:"), gbc);
|
||||
|
||||
oldPasswordField = new JPasswordField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(oldPasswordField, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
panel.add(new JLabel("新密码:"), gbc);
|
||||
|
||||
newPassword1Field = new JPasswordField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(newPassword1Field, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
panel.add(new JLabel("确认新密码:"), gbc);
|
||||
|
||||
newPassword2Field = new JPasswordField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(newPassword2Field, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
confirmButton = new JButton("确认修改");
|
||||
confirmButton.setPreferredSize(new Dimension(120, 30));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
panel.add(confirmButton, gbc);
|
||||
|
||||
add(panel);
|
||||
|
||||
confirmButton.addActionListener(e -> handleConfirm());
|
||||
getRootPane().setDefaultButton(confirmButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理确认修改
|
||||
*/
|
||||
private void handleConfirm() {
|
||||
String oldPassword = new String(oldPasswordField.getPassword());
|
||||
String newPassword1 = new String(newPassword1Field.getPassword());
|
||||
String newPassword2 = new String(newPassword2Field.getPassword());
|
||||
|
||||
if (oldPassword.isEmpty() || newPassword1.isEmpty() ||
|
||||
newPassword2.isEmpty()) {
|
||||
showError("所有字段都不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newPassword1.equals(newPassword2)) {
|
||||
showError("两次输入的新密码不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PASSWORD_PATTERN.matcher(newPassword1).matches()) {
|
||||
showError("新密码不符合要求:\n" +
|
||||
"长度6-10位, 必须包含大小写字母和数字, 且不能包含特殊字符");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userManager.changePassword(email, oldPassword, newPassword1)) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"密码修改成功!",
|
||||
"成功",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
dispose();
|
||||
} else {
|
||||
showError("原密码错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误信息
|
||||
*
|
||||
* @param message 错误消息
|
||||
*/
|
||||
private void showError(String message) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
message,
|
||||
"错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package ui;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* 数学学习软件主入口
|
||||
*
|
||||
* @author 结对项目
|
||||
* @version 1.0
|
||||
*/
|
||||
public class MathLearningApp {
|
||||
|
||||
/**
|
||||
* 程序主入口
|
||||
*
|
||||
* @param args 命令行参数
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
LoginFrame loginFrame = new LoginFrame();
|
||||
loginFrame.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,215 @@
|
||||
package ui;
|
||||
|
||||
import model.Grade;
|
||||
import model.UserManager;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.io.Serial;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 密码设置对话框
|
||||
* 用于新用户注册时设置密码和选择年级
|
||||
*/
|
||||
public class PasswordSetupDialog extends JDialog {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Pattern PASSWORD_PATTERN =
|
||||
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z0-9]{6,10}$");
|
||||
|
||||
private final JTextField usernameField;
|
||||
private final JPasswordField password1Field;
|
||||
private final JPasswordField password2Field;
|
||||
private final JComboBox<String> gradeComboBox;
|
||||
private final JButton confirmButton;
|
||||
private final UserManager userManager;
|
||||
private final String email;
|
||||
private boolean registrationComplete;
|
||||
|
||||
/**
|
||||
* 构造密码设置对话框
|
||||
*
|
||||
* @param parent 父窗口
|
||||
* @param userManager 用户管理器
|
||||
* @param email 用户邮箱
|
||||
*/
|
||||
public PasswordSetupDialog(Frame parent, UserManager userManager, String email) {
|
||||
super(parent, "设置密码和用户名", true);
|
||||
this.userManager = userManager;
|
||||
this.email = email;
|
||||
this.registrationComplete = false;
|
||||
|
||||
setSize(600, 420);
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(true);
|
||||
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel infoLabel = new JLabel("<html>密码要求:<br>" +
|
||||
"1. 长度 6-10位<br>" +
|
||||
"2. 必须包含大写字母<br>" +
|
||||
"3. 必须包含小写字母<br>" +
|
||||
"4. 必须包含数字<br>" +
|
||||
"5. 不能包含其他特殊字符</html>");
|
||||
infoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 11));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
panel.add(infoLabel, gbc);
|
||||
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridy = 1;
|
||||
panel.add(new JLabel("输入密码:"), gbc);
|
||||
|
||||
password1Field = new JPasswordField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(password1Field, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
panel.add(new JLabel("确认密码:"), gbc);
|
||||
|
||||
password2Field = new JPasswordField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(password2Field, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
panel.add(new JLabel("设置用户名:"), gbc);
|
||||
|
||||
usernameField = new JTextField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(usernameField, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
panel.add(new JLabel("选择年级:"), gbc);
|
||||
|
||||
String[] grades = {"小学", "初中", "高中"};
|
||||
gradeComboBox = new JComboBox<>(grades);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
panel.add(gradeComboBox, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
|
||||
confirmButton = new JButton("确认注册");
|
||||
confirmButton.setPreferredSize(new Dimension(120, 30));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 5;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
panel.add(confirmButton, gbc);
|
||||
|
||||
add(panel);
|
||||
|
||||
confirmButton.addActionListener(e -> handleConfirm());
|
||||
getRootPane().setDefaultButton(confirmButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理确认注册
|
||||
*/
|
||||
private void handleConfirm() {
|
||||
String password1 = new String(password1Field.getPassword());
|
||||
String password2 = new String(password2Field.getPassword());
|
||||
String username = usernameField.getText().trim();
|
||||
|
||||
if (username.isEmpty() || password1.isEmpty() || password2.isEmpty()) {
|
||||
showError("用户名和密码不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userManager.usernameExists(username)) {
|
||||
showError("该用户名已被使用,请更换一个");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password1.equals(password2)) {
|
||||
showError("两次输入的密码不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PASSWORD_PATTERN.matcher(password1).matches()) {
|
||||
showError("密码不符合要求:\n" +
|
||||
"长度6-10位, 必须包含大小写字母和数字, 且不能包含特殊字符");
|
||||
return;
|
||||
}
|
||||
|
||||
Grade gradeType = getSelectedGradeType();
|
||||
boolean success = userManager.register(email, password1, gradeType, username);
|
||||
|
||||
if (success) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"注册成功!请返回登录。",
|
||||
"成功",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
registrationComplete = true;
|
||||
dispose();
|
||||
} else {
|
||||
showError("注册失败,该邮箱可能已被注册");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选择的年级类型
|
||||
*
|
||||
* @return 年级类型
|
||||
*/
|
||||
private Grade getSelectedGradeType() {
|
||||
String selected = (String) gradeComboBox.getSelectedItem();
|
||||
switch (selected) {
|
||||
case "小学":
|
||||
return Grade.primary;
|
||||
case "初中":
|
||||
return Grade.middle;
|
||||
case "高中":
|
||||
return Grade.high;
|
||||
default:
|
||||
return Grade.primary;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误信息
|
||||
*
|
||||
* @param message 错误消息
|
||||
*/
|
||||
private void showError(String message) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
message,
|
||||
"错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查注册是否完成
|
||||
*
|
||||
* @return 注册是否完成
|
||||
*/
|
||||
public boolean isRegistrationComplete() {
|
||||
return registrationComplete;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,210 @@
|
||||
package ui;
|
||||
|
||||
import model.Grade;
|
||||
import model.Question;
|
||||
import model.QuestionMaker;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 答题界面
|
||||
* 显示题目和选项,接收用户答案
|
||||
*/
|
||||
public class QuizFrame extends JFrame {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String email;
|
||||
private final Grade gradeType;
|
||||
private final List<Question> questions;
|
||||
private int currentIndex;
|
||||
private int correctCount;
|
||||
|
||||
private JLabel questionLabel;
|
||||
private JLabel progressLabel;
|
||||
private JRadioButton[] optionButtons;
|
||||
private JButton submitButton;
|
||||
private ButtonGroup optionGroup;
|
||||
|
||||
/**
|
||||
* 构造答题界面
|
||||
*
|
||||
* @param email 用户邮箱
|
||||
* @param gradeType 年级类型
|
||||
* @param questionCount 题目数量
|
||||
*/
|
||||
public QuizFrame(String email, Grade gradeType, int questionCount) {
|
||||
this.email = email;
|
||||
this.gradeType = gradeType;
|
||||
this.currentIndex = 0;
|
||||
this.correctCount = 0;
|
||||
|
||||
QuestionMaker generator = new QuestionMaker();
|
||||
this.questions = generator.makeQuestions(gradeType, questionCount, email);
|
||||
|
||||
if (questions.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"生成题目失败,返回主界面",
|
||||
"错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
returnToGradeSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
setTitle("数学学习软件 - 答题");
|
||||
setSize(900, 600);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(true);
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
progressLabel = new JLabel("", JLabel.CENTER);
|
||||
progressLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
mainPanel.add(progressLabel, BorderLayout.NORTH);
|
||||
|
||||
JPanel centerPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(10, 10, 10, 10);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
// 新增:答案格式提示
|
||||
JLabel answerFormatLabel = new JLabel("注意:所有计算结果的答案均保留两位小数。", JLabel.CENTER);
|
||||
answerFormatLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
centerPanel.add(answerFormatLabel, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridwidth = 2;
|
||||
|
||||
questionLabel = new JLabel();
|
||||
questionLabel.setFont(new Font("Cambria Math", Font.PLAIN, 18));
|
||||
questionLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 20, 10));
|
||||
centerPanel.add(questionLabel, gbc);
|
||||
|
||||
optionGroup = new ButtonGroup();
|
||||
optionButtons = new JRadioButton[4];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i] = new JRadioButton();
|
||||
optionButtons[i].setFont(new Font("Cambria Math", Font.PLAIN, 14));
|
||||
optionGroup.add(optionButtons[i]);
|
||||
gbc.gridy = i + 2;
|
||||
gbc.gridwidth = 2;
|
||||
centerPanel.add(optionButtons[i], gbc);
|
||||
gbc.gridwidth = 1;
|
||||
}
|
||||
|
||||
mainPanel.add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
||||
submitButton = new JButton("提交答案");
|
||||
submitButton.setPreferredSize(new Dimension(150, 35));
|
||||
submitButton.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
bottomPanel.add(submitButton);
|
||||
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
submitButton.addActionListener(e -> handleSubmit());
|
||||
|
||||
displayQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示当前题目
|
||||
*/
|
||||
private void displayQuestion() {
|
||||
if (currentIndex >= questions.size()) {
|
||||
showResult();
|
||||
return;
|
||||
}
|
||||
|
||||
Question question = questions.get(currentIndex);
|
||||
progressLabel.setText(String.format("第 %d/%d 题",
|
||||
currentIndex + 1, questions.size()));
|
||||
questionLabel.setText("<html><body style='width: 500px'>" +
|
||||
question.getQuestionText() + "</body></html>");
|
||||
|
||||
optionGroup.clearSelection();
|
||||
|
||||
String[] options = question.getOptions();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i].setText(options[i]);
|
||||
}
|
||||
|
||||
submitButton.setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理答案提交
|
||||
*/
|
||||
private void handleSubmit() {
|
||||
int selectedIndex = -1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (optionButtons[i].isSelected()) {
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedIndex == -1) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"请选择一个答案",
|
||||
"提示",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
Question question = questions.get(currentIndex);
|
||||
if (selectedIndex == question.getCorrectAnswer()) {
|
||||
correctCount++;
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
displayQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示答题结果
|
||||
*/
|
||||
private void showResult() {
|
||||
double percentage = (double) correctCount / questions.size() * 100;
|
||||
int score = (int) percentage;
|
||||
|
||||
ResultFrame resultFrame = new ResultFrame(
|
||||
email, gradeType, score, correctCount, questions.size());
|
||||
resultFrame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回年级选择界面
|
||||
*/
|
||||
private void returnToGradeSelection() {
|
||||
GradeSelectionFrame frame = new GradeSelectionFrame(email, gradeType);
|
||||
frame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package ui;
|
||||
|
||||
import model.Grade;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
|
||||
/**
|
||||
* 答题结果展示界面
|
||||
* 显示得分和统计信息,提供继续答题或退出选项
|
||||
*/
|
||||
public class ResultFrame extends JFrame {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String email;
|
||||
private final Grade gradeType;
|
||||
|
||||
/**
|
||||
* 构造结果展示界面
|
||||
*
|
||||
* @param email 用户邮箱
|
||||
* @param gradeType 年级类型
|
||||
* @param score 得分
|
||||
* @param correctCount 答对题数
|
||||
* @param totalCount 总题数
|
||||
*/
|
||||
public ResultFrame(String email, Grade gradeType, int score,
|
||||
int correctCount, int totalCount) {
|
||||
this.email = email;
|
||||
this.gradeType = gradeType;
|
||||
|
||||
setTitle("数学学习软件 - 答题结果");
|
||||
setSize(600, 420);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(true);
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
|
||||
|
||||
JPanel centerPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(10, 10, 10, 10);
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
|
||||
JLabel titleLabel = new JLabel("答题完成!", JLabel.CENTER);
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
||||
centerPanel.add(titleLabel, gbc);
|
||||
|
||||
gbc.gridy = 1;
|
||||
JLabel scoreLabel = new JLabel("得分: " + score, JLabel.CENTER);
|
||||
scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 36));
|
||||
scoreLabel.setForeground(getScoreColor(score));
|
||||
centerPanel.add(scoreLabel, gbc);
|
||||
|
||||
gbc.gridy = 2;
|
||||
JLabel detailLabel = new JLabel(
|
||||
String.format("答对 %d 题 / 共 %d 题", correctCount, totalCount),
|
||||
JLabel.CENTER);
|
||||
detailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
centerPanel.add(detailLabel, gbc);
|
||||
|
||||
mainPanel.add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 0));
|
||||
JButton continueButton = new JButton("继续做题");
|
||||
JButton exitButton = new JButton("退出");
|
||||
|
||||
continueButton.setPreferredSize(new Dimension(120, 35));
|
||||
exitButton.setPreferredSize(new Dimension(120, 35));
|
||||
|
||||
buttonPanel.add(continueButton);
|
||||
buttonPanel.add(exitButton);
|
||||
|
||||
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
continueButton.addActionListener(e -> continueQuiz());
|
||||
exitButton.addActionListener(e -> exitToSelection());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分数返回对应颜色
|
||||
*
|
||||
* @param score 分数
|
||||
* @return 颜色对象
|
||||
*/
|
||||
private Color getScoreColor(int score) {
|
||||
if (score >= 90) {
|
||||
return new Color(0, 150, 0);
|
||||
} else if (score >= 60) {
|
||||
return new Color(255, 140, 0);
|
||||
} else {
|
||||
return new Color(200, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续做题
|
||||
*/
|
||||
private void continueQuiz() {
|
||||
GradeSelectionFrame frame = new GradeSelectionFrame(email, gradeType);
|
||||
frame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出到选择界面
|
||||
*/
|
||||
private void exitToSelection() {
|
||||
GradeSelectionFrame frame = new GradeSelectionFrame(email, gradeType);
|
||||
frame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
package ui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* 用户设置对话框
|
||||
* 显示用户信息并提供修改密码的入口
|
||||
*/
|
||||
public class UserSettingsDialog extends JDialog {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String email;
|
||||
private final String username;
|
||||
private final Frame parentFrame;
|
||||
|
||||
/**
|
||||
* 构造用户设置对话框
|
||||
*
|
||||
* @param parent 父窗口
|
||||
* @param email 用户邮箱
|
||||
* @param username 用户名
|
||||
*/
|
||||
public UserSettingsDialog(Frame parent, String email, String username) {
|
||||
super(parent, "用户设置", true);
|
||||
this.parentFrame = parent;
|
||||
this.email = email;
|
||||
this.username = username;
|
||||
|
||||
setSize(400, 250);
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(false);
|
||||
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(10, 5, 10, 5);
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
|
||||
// 显示用户名
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
panel.add(new JLabel("用户名:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
JLabel usernameLabel = new JLabel(username);
|
||||
usernameLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
panel.add(usernameLabel, gbc);
|
||||
|
||||
// 显示邮箱
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
panel.add(new JLabel("邮箱:"), gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
JLabel emailLabel = new JLabel(email);
|
||||
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
panel.add(emailLabel, gbc);
|
||||
|
||||
// 修改密码按钮
|
||||
JButton changePasswordButton = new JButton("修改密码");
|
||||
changePasswordButton.setPreferredSize(new Dimension(120, 30));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
gbc.insets = new Insets(20, 5, 10, 5);
|
||||
panel.add(changePasswordButton, gbc);
|
||||
|
||||
add(panel);
|
||||
|
||||
// 按钮点击事件
|
||||
changePasswordButton.addActionListener(e -> openChangePasswordDialog());
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开修改密码对话框
|
||||
*/
|
||||
private void openChangePasswordDialog() {
|
||||
// 关闭当前对话框
|
||||
this.dispose();
|
||||
// 打开修改密码对话框
|
||||
ChangePasswordDialog dialog = new ChangePasswordDialog(parentFrame, email);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue