Compare commits
2 Commits
main
...
zhang_bran
| Author | SHA1 | Date |
|---|---|---|
|
|
f44bbbf769 | 5 months ago |
|
|
f3222a487e | 5 months ago |
@ -0,0 +1,7 @@
|
||||
package model;
|
||||
|
||||
public enum Grade {
|
||||
primary,
|
||||
middle,
|
||||
high
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package model;
|
||||
|
||||
public class HighMaker extends Student {
|
||||
final static String[] binaryOps = {"+", "-", "*", "/"}; // 操作符
|
||||
final static String[] trigOps = {"sin", "cos", "tan"};
|
||||
int operandCount;
|
||||
String[] questionParts; // 操作数
|
||||
boolean[] specialOpFlags; // 特殊操作符索引序列
|
||||
int parenStart;
|
||||
int parenEnd;
|
||||
|
||||
public HighMaker(String name, String password, String path) {
|
||||
super(name, password, path);
|
||||
}
|
||||
|
||||
// 数据预处理
|
||||
public void getRandom() {
|
||||
this.operandCount = random.nextInt(4) + 2;
|
||||
this.questionParts = new String[this.operandCount];
|
||||
|
||||
// 使用基类方法随机决定特殊操作符的数量和位置
|
||||
this.specialOpFlags = randomMaker(this.operandCount);
|
||||
|
||||
// 生成题目各部分
|
||||
for (int i = 0; i < operandCount; i++) {
|
||||
int operand = random.nextInt(100) + 1;
|
||||
if (specialOpFlags[i]) {
|
||||
String op = trigOps[random.nextInt(trigOps.length)];
|
||||
questionParts[i] = op + "(" + operand + ")";
|
||||
} else {
|
||||
questionParts[i] = String.valueOf(operand);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用基类方法生成有效括号
|
||||
int[] parenPos = bracketMaker(this.operandCount);
|
||||
this.parenStart = parenPos[0];
|
||||
this.parenEnd = parenPos[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String makeOneQuestion() {
|
||||
getRandom();
|
||||
StringBuilder question = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < operandCount; i++) {
|
||||
if (i == parenStart) {
|
||||
question.append("(");
|
||||
}
|
||||
question.append(questionParts[i]);
|
||||
if (i == parenEnd) {
|
||||
question.append(")");
|
||||
}
|
||||
|
||||
if (i < operandCount - 1) {
|
||||
String op = binaryOps[random.nextInt(binaryOps.length)];
|
||||
question.append(" ").append(op).append(" ");
|
||||
}
|
||||
}
|
||||
return question.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package model;
|
||||
|
||||
public class MiddleMaker extends Student {
|
||||
final static String[] binaryOps = {"+", "-", "*", "/"}; // 操作符
|
||||
final static String[] unaryOps = {"^2", "√"};
|
||||
int operandCount;
|
||||
String[] questionParts; // 操作数
|
||||
boolean[] specialOpFlags; // 特殊操作符索引序列
|
||||
int parenStart;
|
||||
int parenEnd;
|
||||
|
||||
public MiddleMaker(String name, String password, String path) {
|
||||
super(name, password, path);
|
||||
}
|
||||
|
||||
// 数据预处理
|
||||
public void getRandom() {
|
||||
this.operandCount = random.nextInt(4) + 2;
|
||||
this.questionParts = new String[this.operandCount];
|
||||
|
||||
// 使用基类方法随机决定特殊操作符的数量和位置
|
||||
this.specialOpFlags = randomMaker(this.operandCount);
|
||||
|
||||
// 生成题目各部分
|
||||
for (int i = 0; i < this.operandCount; i++) {
|
||||
int operand = random.nextInt(100) + 1;
|
||||
if (this.specialOpFlags[i]) {
|
||||
String op = unaryOps[random.nextInt(unaryOps.length)];
|
||||
if (op.equals("√")) {
|
||||
this.questionParts[i] = op + operand;
|
||||
} else {
|
||||
this.questionParts[i] = operand + op;
|
||||
}
|
||||
} else {
|
||||
this.questionParts[i] = String.valueOf(operand);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用基类方法生成有效括号
|
||||
int[] parenPos = bracketMaker(this.operandCount);
|
||||
this.parenStart = parenPos[0];
|
||||
this.parenEnd = parenPos[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String makeOneQuestion() {
|
||||
getRandom();
|
||||
StringBuilder question = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < operandCount; i++) {
|
||||
if (i == parenStart) {
|
||||
question.append("(");
|
||||
}
|
||||
question.append(questionParts[i]);
|
||||
if (i == parenEnd) {
|
||||
question.append(")");
|
||||
}
|
||||
if (i < operandCount - 1) {
|
||||
question.append(" ").append(binaryOps[random.nextInt(binaryOps.length)]).append(" ");
|
||||
}
|
||||
}
|
||||
return question.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package model;
|
||||
|
||||
public class PrimaryMaker extends Student {
|
||||
final static char[] operators = {'+', '-', '*', '/'}; // 操作符
|
||||
int operandCount; // 操作数个数
|
||||
int[] operands; // 操作数列表
|
||||
int parenStart; // 左括号索引
|
||||
int parenEnd; // 右括号
|
||||
|
||||
public PrimaryMaker(String name, String password, String path) {
|
||||
super(name, password, path);
|
||||
}
|
||||
|
||||
// 数据预处理
|
||||
public void getRandom() {
|
||||
// 随机决定操作数个数
|
||||
this.operandCount = random.nextInt(4) + 2;
|
||||
this.operands = new int[operandCount];
|
||||
for (int j = 0; j < operandCount; j++) {
|
||||
operands[j] = random.nextInt(100) + 1;
|
||||
}
|
||||
|
||||
// 使用基类方法生成有效括号
|
||||
int[] parenPos = bracketMaker(this.operandCount);
|
||||
this.parenStart = parenPos[0];
|
||||
this.parenEnd = parenPos[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String makeOneQuestion() {
|
||||
getRandom();
|
||||
StringBuilder question = new StringBuilder();
|
||||
|
||||
for (int j = 0; j < this.operandCount; j++) {
|
||||
if (j == this.parenStart) {
|
||||
question.append("(");
|
||||
}
|
||||
question.append(operands[j]);
|
||||
if (j == this.parenEnd) {
|
||||
question.append(")");
|
||||
}
|
||||
|
||||
if (j < this.operandCount - 1) {
|
||||
char op = operators[random.nextInt(operators.length)];
|
||||
if (op == '/') {
|
||||
this.operands[j + 1] = this.operands[j + 1] == 0 ? 1 : this.operands[j + 1];
|
||||
}
|
||||
question.append(" ").append(op).append(" ");
|
||||
}
|
||||
}
|
||||
return question.toString();
|
||||
}
|
||||
}
|
||||
@ -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,61 @@
|
||||
package model;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public abstract class Student {
|
||||
protected String name;
|
||||
protected String password;
|
||||
protected String path;
|
||||
protected Random random;
|
||||
|
||||
public Student(String name, String password, String path) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.path = path;
|
||||
this.random = new Random();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成有效的括号位置
|
||||
* 确保括号不会包围整个表达式
|
||||
*
|
||||
* @param operands 操作数数量
|
||||
* @return 包含起始和结束位置的数组 [parenStart, parenEnd]
|
||||
*/
|
||||
protected int[] bracketMaker(int operands) {
|
||||
boolean useParen = operands > 2 && random.nextBoolean();
|
||||
int parenStart = 0;
|
||||
int parenEnd = operands - 1;
|
||||
|
||||
while (parenStart == 0 && parenEnd == operands - 1) {
|
||||
parenStart = useParen ? random.nextInt(operands - 1) : -2;
|
||||
parenEnd = useParen ? random.nextInt(operands - 1 - parenStart) + parenStart + 1 : -2;
|
||||
}
|
||||
|
||||
return new int[]{parenStart, parenEnd};
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机选择特殊操作符的位置
|
||||
*
|
||||
* @param operands 操作数数量
|
||||
* @return 标记特殊操作符位置的布尔数组
|
||||
*/
|
||||
protected boolean[] randomMaker(int operands) {
|
||||
int specialNumber = Math.min(operands, random.nextInt(operands) + 1);
|
||||
boolean[] specialOpFlags = new boolean[operands];
|
||||
|
||||
for (int i = 0; i < specialNumber; i++) {
|
||||
int pos;
|
||||
do {
|
||||
pos = random.nextInt(operands);
|
||||
} while (specialOpFlags[pos]);
|
||||
specialOpFlags[pos] = true;
|
||||
}
|
||||
|
||||
return specialOpFlags;
|
||||
}
|
||||
|
||||
protected abstract String makeOneQuestion();
|
||||
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
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).{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(400, 300);
|
||||
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(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel infoLabel = new JLabel("<html>新密码要求:<br>" +
|
||||
"长度6-10位,必须包含大小写字母和数字</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(15);
|
||||
gbc.gridx = 1;
|
||||
panel.add(oldPasswordField, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
panel.add(new JLabel("新密码:"), gbc);
|
||||
|
||||
newPassword1Field = new JPasswordField(15);
|
||||
gbc.gridx = 1;
|
||||
panel.add(newPassword1Field, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
panel.add(new JLabel("确认新密码:"), gbc);
|
||||
|
||||
newPassword2Field = new JPasswordField(15);
|
||||
gbc.gridx = 1;
|
||||
panel.add(newPassword2Field, gbc);
|
||||
|
||||
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,191 @@
|
||||
package ui;
|
||||
|
||||
import model.Grade;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 年级选择和题目数量输入界面
|
||||
* 用户可以选择年级、设置题目数量、修改密码或退出登录
|
||||
*/
|
||||
public class GradeSelectionFrame extends JFrame {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String email;
|
||||
private Grade currentGrade;
|
||||
private final JLabel gradeLabel;
|
||||
private final JComboBox<String> gradeComboBox;
|
||||
private final JSpinner questionSpinner;
|
||||
private final JButton startButton;
|
||||
private final JButton changePasswordButton;
|
||||
private final JButton logoutButton;
|
||||
|
||||
/**
|
||||
* 构造年级选择界面
|
||||
*
|
||||
* @param email 用户邮箱
|
||||
* @param initialGrade 初始年级
|
||||
*/
|
||||
public GradeSelectionFrame(String email, Grade initialGrade) {
|
||||
this.email = email;
|
||||
this.currentGrade = initialGrade;
|
||||
|
||||
setTitle("数学学习软件 - 选择年级");
|
||||
setSize(450, 350);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
JPanel topPanel = new JPanel(new BorderLayout());
|
||||
JLabel welcomeLabel = new JLabel("欢迎, " + email, JLabel.CENTER);
|
||||
welcomeLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
|
||||
topPanel.add(welcomeLabel, BorderLayout.NORTH);
|
||||
|
||||
gradeLabel = new JLabel("当前年级: " + getGradeName(currentGrade),
|
||||
JLabel.CENTER);
|
||||
gradeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
||||
gradeLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
|
||||
topPanel.add(gradeLabel, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
JPanel centerPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(10, 5, 10, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
centerPanel.add(new JLabel("选择年级:"), gbc);
|
||||
|
||||
String[] grades = {"小学", "初中", "高中"};
|
||||
gradeComboBox = new JComboBox<>(grades);
|
||||
gradeComboBox.setSelectedItem(getGradeName(currentGrade));
|
||||
gbc.gridx = 1;
|
||||
centerPanel.add(gradeComboBox, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
centerPanel.add(new JLabel("题目数量:"), gbc);
|
||||
|
||||
SpinnerNumberModel spinnerModel = new SpinnerNumberModel(10, 10, 30, 1);
|
||||
questionSpinner = new JSpinner(spinnerModel);
|
||||
gbc.gridx = 1;
|
||||
centerPanel.add(questionSpinner, gbc);
|
||||
|
||||
startButton = new JButton("开始答题");
|
||||
startButton.setPreferredSize(new Dimension(200, 40));
|
||||
startButton.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
centerPanel.add(startButton, gbc);
|
||||
|
||||
mainPanel.add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
|
||||
changePasswordButton = new JButton("修改密码");
|
||||
logoutButton = new JButton("退出登录");
|
||||
bottomPanel.add(changePasswordButton);
|
||||
bottomPanel.add(logoutButton);
|
||||
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
startButton.addActionListener(e -> startQuiz());
|
||||
changePasswordButton.addActionListener(e -> changePassword());
|
||||
logoutButton.addActionListener(e -> logout());
|
||||
gradeComboBox.addActionListener(e -> updateGrade());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前年级
|
||||
*/
|
||||
private void updateGrade() {
|
||||
String selected = (String) gradeComboBox.getSelectedItem();
|
||||
switch (selected) {
|
||||
case "小学":
|
||||
currentGrade = Grade.primary;
|
||||
break;
|
||||
case "初中":
|
||||
currentGrade = Grade.middle;
|
||||
break;
|
||||
case "高中":
|
||||
currentGrade = Grade.high;
|
||||
break;
|
||||
}
|
||||
gradeLabel.setText("当前年级: " + getGradeName(currentGrade));
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始答题
|
||||
*/
|
||||
private void startQuiz() {
|
||||
int questionCount = (Integer) questionSpinner.getValue();
|
||||
QuizFrame quizFrame = new QuizFrame(email, currentGrade, questionCount);
|
||||
quizFrame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
private void changePassword() {
|
||||
ChangePasswordDialog dialog = new ChangePasswordDialog(this, email);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
private void logout() {
|
||||
int confirm = JOptionPane.showConfirmDialog(this,
|
||||
"确定要退出登录吗?",
|
||||
"确认",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
|
||||
if (confirm == JOptionPane.YES_OPTION) {
|
||||
LoginFrame loginFrame = new LoginFrame();
|
||||
loginFrame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取年级名称
|
||||
*
|
||||
* @param grade 年级类型
|
||||
* @return 年级名称
|
||||
*/
|
||||
private String getGradeName(Grade grade) {
|
||||
switch (grade) {
|
||||
case primary:
|
||||
return "小学";
|
||||
case middle:
|
||||
return "初中";
|
||||
case high:
|
||||
return "高中";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
package ui;
|
||||
|
||||
import model.Grade;
|
||||
import model.UserManager;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
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.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
|
||||
/**
|
||||
* 登录和注册界面
|
||||
* 提供用户登录和注册入口
|
||||
*/
|
||||
public class LoginFrame extends JFrame {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final JTextField emailField;
|
||||
private final JPasswordField passwordField;
|
||||
private final JButton loginButton;
|
||||
private final JButton registerButton;
|
||||
private final UserManager userManager;
|
||||
|
||||
/**
|
||||
* 构造登录界面
|
||||
*/
|
||||
public LoginFrame() {
|
||||
this.userManager = new UserManager();
|
||||
|
||||
setTitle("数学学习软件 - 登录");
|
||||
setSize(400, 300);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
|
||||
JPanel mainPanel = new JPanel(new GridBagLayout());
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel titleLabel = new JLabel("小初高数学学习系统", JLabel.CENTER);
|
||||
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
mainPanel.add(titleLabel, gbc);
|
||||
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridy = 1;
|
||||
mainPanel.add(new JLabel("邮箱:"), gbc);
|
||||
|
||||
emailField = new JTextField(20);
|
||||
gbc.gridx = 1;
|
||||
mainPanel.add(emailField, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
mainPanel.add(new JLabel("密码:"), gbc);
|
||||
|
||||
passwordField = new JPasswordField(20);
|
||||
gbc.gridx = 1;
|
||||
mainPanel.add(passwordField, gbc);
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
|
||||
loginButton = new JButton("登录");
|
||||
registerButton = new JButton("注册");
|
||||
|
||||
loginButton.setPreferredSize(new Dimension(100, 30));
|
||||
registerButton.setPreferredSize(new Dimension(100, 30));
|
||||
|
||||
buttonPanel.add(loginButton);
|
||||
buttonPanel.add(registerButton);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
gbc.gridwidth = 2;
|
||||
mainPanel.add(buttonPanel, gbc);
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
loginButton.addActionListener(e -> handleLogin());
|
||||
registerButton.addActionListener(e -> handleRegister());
|
||||
|
||||
getRootPane().setDefaultButton(loginButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户登录
|
||||
*/
|
||||
private void handleLogin() {
|
||||
String email = emailField.getText().trim();
|
||||
String password = new String(passwordField.getPassword());
|
||||
|
||||
if (email.isEmpty() || password.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"邮箱和密码不能为空",
|
||||
"错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
Grade grade = userManager.login(email, password);
|
||||
if (grade != null) {
|
||||
openGradeSelection(email, grade);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"邮箱或密码错误",
|
||||
"登录失败",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开注册对话框
|
||||
*/
|
||||
private void handleRegister() {
|
||||
RegisterDialog dialog = new RegisterDialog(this, userManager);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开年级选择界面
|
||||
*
|
||||
* @param email 用户邮箱
|
||||
* @param initialGrade 初始年级
|
||||
*/
|
||||
private void openGradeSelection(String email, Grade initialGrade) {
|
||||
GradeSelectionFrame gradeFrame = new GradeSelectionFrame(email, initialGrade);
|
||||
gradeFrame.setVisible(true);
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
@ -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,190 @@
|
||||
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 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).{6,10}$");
|
||||
|
||||
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(400, 300);
|
||||
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(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel infoLabel = new JLabel("<html>密码要求:<br>" +
|
||||
"1. 长度6-10位<br>" +
|
||||
"2. 必须包含大写字母<br>" +
|
||||
"3. 必须包含小写字母<br>" +
|
||||
"4. 必须包含数字</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(15);
|
||||
gbc.gridx = 1;
|
||||
panel.add(password1Field, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
panel.add(new JLabel("确认密码:"), gbc);
|
||||
|
||||
password2Field = new JPasswordField(15);
|
||||
gbc.gridx = 1;
|
||||
panel.add(password2Field, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
panel.add(new JLabel("选择年级:"), gbc);
|
||||
|
||||
String[] grades = {"小学", "初中", "高中"};
|
||||
gradeComboBox = new JComboBox<>(grades);
|
||||
gbc.gridx = 1;
|
||||
panel.add(gradeComboBox, gbc);
|
||||
|
||||
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 password1 = new String(password1Field.getPassword());
|
||||
String password2 = new String(password2Field.getPassword());
|
||||
|
||||
if (password1.isEmpty() || password2.isEmpty()) {
|
||||
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);
|
||||
|
||||
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,197 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 构造答题界面
|
||||
*
|
||||
* @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(600, 450);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
|
||||
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;
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
|
||||
questionLabel = new JLabel();
|
||||
questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
|
||||
questionLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 20, 10));
|
||||
centerPanel.add(questionLabel, gbc);
|
||||
|
||||
ButtonGroup 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]);
|
||||
gbc.gridy = i + 1;
|
||||
centerPanel.add(optionButtons[i], gbc);
|
||||
}
|
||||
|
||||
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>");
|
||||
|
||||
String[] options = question.getOptions();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i].setText(options[i]);
|
||||
optionButtons[i].setSelected(false);
|
||||
}
|
||||
|
||||
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,177 @@
|
||||
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.JTextField;
|
||||
import java.awt.Frame;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 用户注册对话框
|
||||
* 实现邮箱验证码注册功能
|
||||
*/
|
||||
public class RegisterDialog extends JDialog {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Pattern EMAIL_PATTERN =
|
||||
Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
|
||||
|
||||
private final JTextField emailField;
|
||||
private final JTextField codeField;
|
||||
private final JButton sendCodeButton;
|
||||
private final JButton verifyButton;
|
||||
private final UserManager userManager;
|
||||
private String generatedCode;
|
||||
|
||||
/**
|
||||
* 构造注册对话框
|
||||
*
|
||||
* @param parent 父窗口
|
||||
* @param userManager 用户管理器
|
||||
*/
|
||||
public RegisterDialog(Frame parent, UserManager userManager) {
|
||||
super(parent, "用户注册", true);
|
||||
this.userManager = userManager;
|
||||
|
||||
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(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
panel.add(new JLabel("邮箱:"), gbc);
|
||||
|
||||
emailField = new JTextField(20);
|
||||
gbc.gridx = 1;
|
||||
gbc.gridwidth = 2;
|
||||
panel.add(emailField, gbc);
|
||||
|
||||
sendCodeButton = new JButton("发送验证码");
|
||||
gbc.gridx = 3;
|
||||
gbc.gridwidth = 1;
|
||||
panel.add(sendCodeButton, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
panel.add(new JLabel("验证码:"), gbc);
|
||||
|
||||
codeField = new JTextField(20);
|
||||
codeField.setEnabled(false);
|
||||
gbc.gridx = 1;
|
||||
gbc.gridwidth = 2;
|
||||
panel.add(codeField, gbc);
|
||||
|
||||
verifyButton = new JButton("验证");
|
||||
verifyButton.setEnabled(false);
|
||||
gbc.gridx = 3;
|
||||
gbc.gridwidth = 1;
|
||||
panel.add(verifyButton, gbc);
|
||||
|
||||
add(panel);
|
||||
|
||||
sendCodeButton.addActionListener(e -> sendVerificationCode());
|
||||
verifyButton.addActionListener(e -> verifyCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
*/
|
||||
private void sendVerificationCode() {
|
||||
String email = emailField.getText().trim();
|
||||
|
||||
if (email.isEmpty()) {
|
||||
showError("请输入邮箱地址");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EMAIL_PATTERN.matcher(email).matches()) {
|
||||
showError("邮箱格式不正确");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userManager.userExists(email)) {
|
||||
showError("该邮箱已注册");
|
||||
return;
|
||||
}
|
||||
|
||||
generatedCode = generateCode();
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"验证码已发送: " + generatedCode + "\n(实际应用中会发送到邮箱)",
|
||||
"验证码",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
emailField.setEnabled(false);
|
||||
sendCodeButton.setEnabled(false);
|
||||
codeField.setEnabled(true);
|
||||
verifyButton.setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证验证码
|
||||
*/
|
||||
private void verifyCode() {
|
||||
String inputCode = codeField.getText().trim();
|
||||
|
||||
if (inputCode.isEmpty()) {
|
||||
showError("请输入验证码");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inputCode.equals(generatedCode)) {
|
||||
showError("验证码错误");
|
||||
return;
|
||||
}
|
||||
|
||||
openPasswordDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开密码设置对话框
|
||||
*/
|
||||
private void openPasswordDialog() {
|
||||
String email = emailField.getText().trim();
|
||||
PasswordSetupDialog passwordDialog = new PasswordSetupDialog(
|
||||
(Frame) getOwner(),
|
||||
userManager,
|
||||
email);
|
||||
passwordDialog.setVisible(true);
|
||||
|
||||
if (passwordDialog.isRegistrationComplete()) {
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成6位数字验证码
|
||||
*
|
||||
* @return 验证码字符串
|
||||
*/
|
||||
private String generateCode() {
|
||||
return String.format("%06d", (int) (Math.random() * 1000000));
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误信息
|
||||
*
|
||||
* @param message 错误消息
|
||||
*/
|
||||
private void showError(String message) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
message,
|
||||
"错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@ -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(400, 300);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue