pull/1/head
宋奇峰 2 months ago
parent 41ecae7f5d
commit 079c35c719

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

@ -0,0 +1,19 @@
<component name="libraryTable">
<library name="lib">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/javax.ejb.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.servlet.jsp.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.transaction.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jakarta.mail-2.0.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.resource.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.servlet.jsp.jstl.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.jms.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.persistence.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.annotation.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javax.servlet.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jakarta.activation-2.0.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="openjdk-23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/mathStudyAPP.iml" filepath="$PROJECT_DIR$/mathStudyAPP.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -1,20 +0,0 @@
1. 37 + 35
2. 82 * 28
3. (16 / 56) + 42
4. 6 * 52 + 35
5. (99 + 26) + 37
6. 40 * 63
7. 52 - 61 - 16 - 70 * 51
8. 65 + 83
9. 84 / 64
10. 24 - 47 + 21 + 1 - 33

@ -1,10 +1,12 @@
王五3 123 HIGH wangwu3@example.com
王五2 123 HIGH wangwu2@example.com
123 Aa123456 PRIMARY 2571400460@qq.com
王五1 123 HIGH wangwu1@example.com
scb Aa123456 PRIMARY 3110858122@qq.com
张三3 123 PRIMARY zhangsan3@example.com
张三1 123 PRIMARY zhangsan1@example.com
张三2 123 PRIMARY zhangsan2@example.com
李四3 123 MIDDLE lishi3@example.com
sqf Aa123456 PRIMARY sqf090815@hnu.edu.cn
sqf Qq123456 PRIMARY sqf090815@hnu.edu.cn
李四1 123 MIDDLE lishi1@example.com
李四2 123 MIDDLE lishi2@example.com

@ -8,7 +8,5 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="lib" level="project" />
<orderEntry type="library" name="jakarta.activation-2.0.1 (2)" level="project" />
<orderEntry type="library" name="jakarta.mail-2.0.1 (2)" level="project" />
</component>
</module>

@ -1,4 +1,5 @@
import javax.swing.SwingUtilities;
import view.LoginFrame;
/**

@ -1,18 +0,0 @@
package controller;
import model.Login;
import model.LanguageSwitch;
import view.ExamSetupFrame;
import javax.swing.JOptionPane;
/**
*
*/
public class AssignController {
public void loginSuccess(Login.Account user) {
JOptionPane.showMessageDialog(null,
"登录成功。当前选择为 " + LanguageSwitch.levelToChinese(user.level) + " 出题");
new ExamSetupFrame(user);
}
}

@ -1,16 +1,17 @@
package controller;
import javax.swing.JOptionPane;
import model.Create;
import model.Login;
import model.Paper;
import view.ExamFrame;
import javax.swing.JOptionPane;
/**
* Create Paper ExamFrame
*/
public class FunctionController {
private Login.Account user;
public FunctionController(Login.Account user) {

@ -1,44 +1,23 @@
package model;
import java.util.*;
import javax.script.*;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.script.ScriptException;
/**
*
*/
public class Create {
// 把数学表达式转换为 JS 可执行表达式的简单转换器
private static String toJsExpr(String expr) {
String s = expr;
s = s.replaceAll("\\^2", ".pow2"); // 临时占位
s = s.replaceAll("sqrt\\(", "Math.sqrt(");
s = s.replaceAll("sin\\(", "Math.sin(Math.toRadians(");
s = s.replaceAll("cos\\(", "Math.cos(Math.toRadians(");
s = s.replaceAll("tan\\(", "Math.tan(Math.toRadians(");
// 修复 sin(Math.toRadians(x) 末尾多一层 ) -> we need to close paren
// Simple approach: for each "Math.sin(Math.toRadians(", there will be following number or parenthesis, we close later with "))"
// Replace our .pow2 placeholder into Math.pow(x,2)
// Handle pow2: we earlier converted x^2 -> x.pow2, so replace (\d+|...) .pow2 to Math.pow(that,2)
// For simplicity, replace pattern "([0-9)]+)\\.pow2" with "Math.pow($1,2)" — approximate
s = s.replaceAll("(\\d+)\\.pow2", "Math.pow($1,2)");
// If there are parentheses before .pow2: ") .pow2" unlikely; accept some limitations.
// For sin/cos/tan we changed 'sin(' -> 'Math.sin(Math.toRadians(' so must add two closing ) after the argument.
// To keep it simple, replace "Math.sin(Math.toRadians(" -> "Math.sin(Math.toRadians(" and later we will ensure parentheses in evaluation by appending "))" when needed.
// We'll also replace any leftover "^2" directly
s = s.replaceAll("\\^2", "");
// Finally, replace any multiple spaces
s = s.replaceAll("\\s+", "");
return s;
}
// 评估表达式数值double若失败抛异常
// 评估表达式数值double若失败抛异常
private static double evalExpression(String expr) throws ScriptException {
return ExpressionEvaluator.evaluate(expr);
}
/**
* Paper questions, options, correctIndex
*/
@ -78,7 +57,6 @@ public class Create {
// 判断是否为整数答案
boolean isIntAnswer = Math.abs(correctVal - Math.round(correctVal)) < 1e-6;
// 产生三个干扰值
Set<String> optsSet = new LinkedHashSet<>();
optsSet.add(correctStr);
@ -90,7 +68,9 @@ public class Create {
if (isIntAnswer) {
// 整数题:干扰项也是整数(偏差 ±1~±10
delta = r.nextInt(10) + 1;
if (r.nextBoolean()) delta = -delta;
if (r.nextBoolean()) {
delta = -delta;
}
} else {
// 小数题:随机偏差比例
delta = (Math.abs(correctVal) < 1e-6)
@ -122,7 +102,9 @@ public class Create {
try {
// 为保存,我们保存原题干列表(文本)
java.util.List<String> raw = new ArrayList<>();
for (String s : paper) raw.add(s);
for (String s : paper) {
raw.add(s);
}
Save.savePaper(user.username, raw);
} catch (RuntimeException re) {
// 抛给 UI 处理(但仍返回 Paper
@ -143,13 +125,17 @@ public class Create {
}
private static int findIndex(String[] arr, String target) {
for (int i = 0; i < arr.length; i++) if (arr[i].equals(target)) return i;
for (int i = 0; i < arr.length; i++) {
if (arr[i].equals(target)) {
return i;
}
}
return 0;
}
private static String formatNumber(double v) {
if (Math.abs(v - Math.round(v)) < 1e-6) {
return String.valueOf((long)Math.round(v));
return String.valueOf((long) Math.round(v));
} else {
return String.format("%.3f", v);
}

@ -1,9 +1,17 @@
package model;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
public class ExpressionEvaluator {
private static final Map<String, Integer> PRECEDENCE = new HashMap<>();
static {
PRECEDENCE.put("+", 1);
PRECEDENCE.put("-", 1);
@ -26,12 +34,12 @@ public class ExpressionEvaluator {
private static List<String> toRPN(String expr) {
List<String> output = new ArrayList<>();
Stack<String> stack = new Stack<>();
// 正则拆分 token数字、函数、符号、括号
StringTokenizer tokenizer = new StringTokenizer(expr, "+-*/^() ", true);
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if (token.isEmpty()) continue;
if (token.isEmpty()) {
continue;
}
if (token.matches("[0-9.]+")) { // 数字
output.add(token);
@ -52,7 +60,6 @@ public class ExpressionEvaluator {
if (!stack.isEmpty() && stack.peek().equals("(")) {
stack.pop();
}
// 如果栈顶是函数,弹出函数
if (!stack.isEmpty() && stack.peek().matches("[a-zA-Z]+")) {
output.add(stack.pop());
}
@ -76,20 +83,39 @@ public class ExpressionEvaluator {
double b = stack.pop();
double a = stack.pop();
switch (token) {
case "+": stack.push(a + b); break;
case "-": stack.push(a - b); break;
case "*": stack.push(a * b); break;
case "/": stack.push(a / b); break;
case "^": stack.push(Math.pow(a, b)); break;
case "+":
stack.push(a + b);
break;
case "-":
stack.push(a - b);
break;
case "*":
stack.push(a * b);
break;
case "/":
stack.push(a / b);
break;
case "^":
stack.push(Math.pow(a, b));
break;
}
} else { // 函数
double a = stack.pop();
switch (token.toLowerCase()) {
case "sin": stack.push(Math.sin(Math.toRadians(a))); break;
case "cos": stack.push(Math.cos(Math.toRadians(a))); break;
case "tan": stack.push(Math.tan(Math.toRadians(a))); break;
case "sqrt": stack.push(Math.sqrt(a)); break;
default: throw new RuntimeException("未知函数: " + token);
case "sin":
stack.push(Math.sin(Math.toRadians(a)));
break;
case "cos":
stack.push(Math.cos(Math.toRadians(a)));
break;
case "tan":
stack.push(Math.tan(Math.toRadians(a)));
break;
case "sqrt":
stack.push(Math.sqrt(a));
break;
default:
throw new RuntimeException("未知函数: " + token);
}
}
}

@ -1,6 +1,11 @@
package model;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -31,7 +36,8 @@ public class Generator extends QuestionGenerator {
}
}
if (generated.size() < n) {
System.out.println("注意:无法生成足够的不重复题目,已生成 " + generated.size() + " 道题(请求 " + n + " 道)");
System.out.println(
"注意:无法生成足够的不重复题目,已生成 " + generated.size() + " 道题(请求 " + n + " 道)");
}
return new ArrayList<>(generated);
}
@ -59,7 +65,8 @@ public class Generator extends QuestionGenerator {
boolean useParens = RAND.nextBoolean();
if (useParens && operands >= 3 && RAND.nextBoolean()) {
sb.append("(");
sb.append(randInt(1, 100)).append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
sb.append(randInt(1, 100)).append(" ").append(randomChoice(ops)).append(" ")
.append(randInt(1, 100));
sb.append(")");
for (int i = 2; i < operands; i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
@ -75,8 +82,11 @@ public class Generator extends QuestionGenerator {
public String genMiddle(int operands) {
String expr = genPrimary(operands);
if (RAND.nextBoolean()) expr = applySquare(expr);
else expr = applySqrt(expr);
if (RAND.nextBoolean()) {
expr = applySquare(expr);
} else {
expr = applySqrt(expr);
}
return expr;
}
@ -88,7 +98,9 @@ public class Generator extends QuestionGenerator {
public String applySquare(String expr) {
List<int[]> spans = findNumberSpans(expr);
if (spans.isEmpty()) return expr + "^2";
if (spans.isEmpty()) {
return expr + "^2";
}
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
@ -98,7 +110,9 @@ public class Generator extends QuestionGenerator {
public String applySqrt(String expr) {
List<int[]> spans = findNumberSpans(expr);
if (spans.isEmpty()) return "sqrt(" + expr + ")";
if (spans.isEmpty()) {
return "sqrt(" + expr + ")";
}
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
@ -109,7 +123,9 @@ public class Generator extends QuestionGenerator {
public String applyTrig(String expr) {
List<int[]> spans = findNumberSpans(expr);
String func = randomChoice(Arrays.asList("sin", "cos", "tan"));
if (spans.isEmpty()) return func + "(" + expr + ")";
if (spans.isEmpty()) {
return func + "(" + expr + ")";
}
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
@ -124,10 +140,14 @@ public class Generator extends QuestionGenerator {
while (i < n) {
if (Character.isDigit(chs[i])) {
int j = i;
while (j < n && (Character.isDigit(chs[j]))) j++;
while (j < n && (Character.isDigit(chs[j]))) {
j++;
}
spans.add(new int[]{i, j});
i = j;
} else i++;
} else {
i++;
}
}
return spans;
}

@ -1,6 +1,7 @@
package model;
public class LanguageSwitch {
public static String levelToChinese(Login.Level l) {
return switch (l) {
case PRIMARY -> "小学";

@ -1,12 +1,16 @@
package model;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.nio.file.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
*
*/
@ -24,7 +28,9 @@ public class LoadFile {
StringBuilder cur = new StringBuilder();
for (String line : lines) {
if (line.matches("^\\s*\\d+\\..*")) {
if (!cur.isEmpty()) all.add(cur.toString().trim());
if (!cur.isEmpty()) {
all.add(cur.toString().trim());
}
cur.setLength(0);
cur.append(line.replaceFirst("^\\s*\\d+\\.", "").trim());
} else {
@ -34,16 +40,21 @@ public class LoadFile {
cur.setLength(0);
}
} else {
if (!cur.isEmpty()) cur.append(" ");
if (!cur.isEmpty()) {
cur.append(" ");
}
cur.append(line.trim());
}
}
}
if (!cur.isEmpty()) all.add(cur.toString().trim());
if (!cur.isEmpty()) {
all.add(cur.toString().trim());
}
}
} catch (IOException e) {
throw new RuntimeException("读取题目文件失败:" + e.getMessage(), e);
}
return all.stream().map(String::trim).filter(s -> !s.isEmpty()).distinct().collect(Collectors.toList());
return all.stream().map(String::trim).filter(s -> !s.isEmpty()).distinct()
.collect(Collectors.toList());
}
}

@ -1,10 +1,14 @@
package model;
import java.util.Map;
import java.util.HashMap;
import java.io.*;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
/**
@ -61,17 +65,30 @@ public class Login {
}
// 注册GUI 调用),若用户名已存在返回 false
// 注册GUI 调用),若用户名或邮箱已存在返回 false
public static synchronized boolean register(String username, String password, Level level,
String email) {
// 检查用户名是否重复
if (accounts.containsKey(username)) {
return false;
}
// 检查邮箱是否重复
for (Account existing : accounts.values()) {
if (existing.email != null && existing.email.equalsIgnoreCase(email)) {
// 邮箱重复,不区分大小写
return false;
}
}
// 都不重复,则注册成功
Account acc = new Account(username, password, level, email);
accounts.put(username, acc);
persistToFile();
return true;
}
// 修改密码(需提供原密码),返回是否成功
public static synchronized boolean changePassword(String username, String oldPwd, String newPwd) {
Account acc = accounts.get(username);

@ -6,6 +6,7 @@ import java.util.List;
*
*/
public class Paper {
public final List<String> questions;
public final List<String[]> options; // 每题 options[i] 长度为4
public final int[] correctIndex; // 每题正确选项下标 0..3

@ -2,17 +2,22 @@ package model;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.nio.charset.StandardCharsets;
public class Save {
public static String savePaper(String username, List<String> paper) {
Path userDir = Paths.get("data", username);
try {
if (!Files.exists(userDir)) Files.createDirectories(userDir);
if (!Files.exists(userDir)) {
Files.createDirectories(userDir);
}
} catch (IOException e) {
throw new RuntimeException("无法创建用户文件夹:" + e.getMessage(), e);
}

@ -1,7 +1,20 @@
package view;
import javax.swing.*;
import java.awt.*;
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 javax.swing.ButtonGroup;
import javax.swing.BorderFactory;
import javax.swing.SwingConstants;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import model.Paper;
import model.Login;
@ -9,6 +22,7 @@ import model.Login;
*
*/
public class ExamFrame extends JFrame {
private Paper paper;
private Login.Account user;
private int index = 0;
@ -35,12 +49,12 @@ public class ExamFrame extends JFrame {
qLabel = new JLabel("", SwingConstants.LEFT);
qLabel.setFont(new Font("Serif", Font.PLAIN, 16));
JPanel top = new JPanel(new BorderLayout());
top.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
top.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
top.add(qLabel, BorderLayout.CENTER);
centerPanel = new JPanel(new GridLayout(4,1,6,6));
centerPanel = new JPanel(new GridLayout(4, 1, 6, 6));
group = new ButtonGroup();
for (int i=0;i<4;i++){
for (int i = 0; i < 4; i++) {
radioBtns[i] = new JRadioButton();
group.add(radioBtns[i]);
centerPanel.add(radioBtns[i]);
@ -63,33 +77,43 @@ public class ExamFrame extends JFrame {
setVisible(true);
}
/** 显示一道题 */
/**
*
*/
private void showQuestion() {
if (index >= paper.size()) {
showSubmitPage();
return;
}
String q = paper.questions.get(index);
qLabel.setText("<html><b>第 " + (index+1) + " 题:</b> " + q + "</html>");
qLabel.setText("<html><b>第 " + (index + 1) + " 题:</b> " + q + "</html>");
String[] opts = paper.options.get(index);
for (int i=0;i<4;i++) {
for (int i = 0; i < 4; i++) {
radioBtns[i].setText(opts[i]);
}
// 清除上一次选择
group.clearSelection();
}
/** 提交当前题 */
/**
*
*/
private void submitAnswer() {
int selected = -1;
for (int i=0;i<4;i++) if (radioBtns[i].isSelected()) selected = i;
for (int i = 0; i < 4; i++) {
if (radioBtns[i].isSelected()) {
selected = i;
}
}
if (selected == -1) {
JOptionPane.showMessageDialog(this, "请选择一个选项后再提交");
return;
}
// 判断对错
if (selected == paper.correctIndex[index]) score++;
if (selected == paper.correctIndex[index]) {
score++;
}
index++;
if (index < paper.size()) {
@ -100,7 +124,9 @@ public class ExamFrame extends JFrame {
}
}
/** 做完所有题目后显示确认提交界面 */
/**
*
*/
private void showSubmitPage() {
// 移除中间题目区域和按钮
getContentPane().remove(centerPanel);
@ -134,10 +160,12 @@ public class ExamFrame extends JFrame {
repaint();
}
/** 完成考试 → 进入成绩界面 */
/**
*
*/
private void finishExam() {
int total = paper.size();
double percent = total==0 ? 0 : (100.0 * score / total);
double percent = total == 0 ? 0 : (100.0 * score / total);
dispose();
new ResultFrame(user, score, total, percent);
}

@ -1,7 +1,16 @@
package view;
import javax.swing.*;
import java.awt.*;
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.JTextField;
import javax.swing.BorderFactory;
import java.awt.GridLayout;
import controller.FunctionController;
import model.Login;
import model.LanguageSwitch;
@ -10,6 +19,7 @@ import model.LanguageSwitch;
* /10-30
*/
public class ExamSetupFrame extends JFrame {
private JTextField numberField;
private JComboBox<String> levelBox;
private FunctionController controller;
@ -24,10 +34,10 @@ public class ExamSetupFrame extends JFrame {
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new GridLayout(4,2,8,8));
p.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
JPanel p = new JPanel(new GridLayout(4, 2, 8, 8));
p.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
p.add(new JLabel("当前难度:"));
levelBox = new JComboBox<>(new String[]{"小学","初中","高中"});
levelBox = new JComboBox<>(new String[]{"小学", "初中", "高中"});
levelBox.setSelectedItem(LanguageSwitch.levelToChinese(user.level));
p.add(levelBox);
@ -56,7 +66,10 @@ public class ExamSetupFrame extends JFrame {
}
});
backBtn.addActionListener(e -> { dispose(); new LoginFrame(); });
backBtn.addActionListener(e -> {
dispose();
new LoginFrame();
});
setVisible(true);
}

@ -1,9 +1,17 @@
package view;
import javax.swing.*;
import java.awt.*;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import javax.swing.BorderFactory;
import model.Login;
import controller.AssignController;
/**
*
@ -20,7 +28,7 @@ public class LoginFrame extends JFrame {
setLocationRelativeTo(null);
JPanel p = new JPanel(new GridLayout(4, 2, 8, 8));
p.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
p.add(new JLabel("用户名:"));
usernameField = new JTextField();
p.add(usernameField);
@ -44,7 +52,8 @@ public class LoginFrame extends JFrame {
String u = usernameField.getText().trim();
String pwd = new String(passwordField.getPassword()).trim();
if (u.isEmpty() || pwd.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入用户名和密码", "提示", JOptionPane.INFORMATION_MESSAGE);
JOptionPane.showMessageDialog(this, "请输入用户名和密码", "提示",
JOptionPane.INFORMATION_MESSAGE);
return;
}
Login.Account acc = Login.login(u, pwd);
@ -52,7 +61,8 @@ public class LoginFrame extends JFrame {
dispose();
new MainMenuFrame(acc); // 登录后先进入主菜单
} else {
JOptionPane.showMessageDialog(this, "用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(this, "用户名或密码错误", "登录失败",
JOptionPane.ERROR_MESSAGE);
}
});

@ -1,56 +1,94 @@
package view;
import javax.swing.*;
import java.awt.*;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.BorderFactory;
import model.Login;
/**
*
* ExamSetupFrame
* ExamSetupFrame
*/
public class MainMenuFrame extends JFrame {
public MainMenuFrame(Login.Account user) {
setTitle("主菜单 - " + user.username);
setSize(400,200);
setSize(400, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new GridLayout(3,1,10,10));
JPanel p = new JPanel(new GridLayout(3, 1, 10, 10));
JButton changePwd = new JButton("修改密码");
JButton start = new JButton("出题设置");
JButton logout = new JButton("退出登录");
p.add(start); p.add(changePwd); p.add(logout);
p.add(start);
p.add(changePwd);
p.add(logout);
add(p);
start.addActionListener(e -> { new ExamSetupFrame(user); dispose(); });
start.addActionListener(e -> {
new ExamSetupFrame(user);
dispose();
});
changePwd.addActionListener(e -> new ChangePasswordDialog(this, user));
logout.addActionListener(e -> { dispose(); new LoginFrame(); });
logout.addActionListener(e -> {
dispose();
new LoginFrame();
});
setVisible(true);
}
// 内部类:修改密码对话框
static class ChangePasswordDialog extends JDialog {
public ChangePasswordDialog(JFrame owner, Login.Account user) {
super(owner, "修改密码", true);
setSize(350,200);
setSize(350, 200);
setLocationRelativeTo(owner);
JPanel p = new JPanel(new GridLayout(4,2,6,6));
p.setBorder(BorderFactory.createEmptyBorder(6,6,6,6));
p.add(new JLabel("旧密码:")); JPasswordField oldp = new JPasswordField(); p.add(oldp);
p.add(new JLabel("新密码:")); JPasswordField newp = new JPasswordField(); p.add(newp);
p.add(new JLabel("再次输入新密码:")); JPasswordField newp2 = new JPasswordField(); p.add(newp2);
JButton ok = new JButton("确定"); JButton cancel = new JButton("取消"); p.add(ok); p.add(cancel);
JPanel p = new JPanel(new GridLayout(4, 2, 6, 6));
p.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
p.add(new JLabel("旧密码:"));
JPasswordField oldp = new JPasswordField();
p.add(oldp);
p.add(new JLabel("新密码:"));
JPasswordField newp = new JPasswordField();
p.add(newp);
p.add(new JLabel("再次输入新密码:"));
JPasswordField newp2 = new JPasswordField();
p.add(newp2);
JButton ok = new JButton("确定");
JButton cancel = new JButton("取消");
p.add(ok);
p.add(cancel);
add(p);
ok.addActionListener(a -> {
String oldPwd = new String(oldp.getPassword()).trim();
String np = new String(newp.getPassword()).trim();
String np2 = new String(newp2.getPassword()).trim();
if (!np.equals(np2)) { JOptionPane.showMessageDialog(this, "两次密码不一致"); return; }
if (!Login.validatePasswordRules(np)) { JOptionPane.showMessageDialog(this, "密码不满足规则"); return; }
if (!np.equals(np2)) {
JOptionPane.showMessageDialog(this, "两次密码不一致");
return;
}
if (!Login.validatePasswordRules(np)) {
JOptionPane.showMessageDialog(this, "密码不满足规则");
return;
}
boolean okr = Login.changePassword(user.username, oldPwd, np);
if (okr) { JOptionPane.showMessageDialog(this, "修改成功"); dispose(); }
else JOptionPane.showMessageDialog(this, "旧密码错误");
if (okr) {
JOptionPane.showMessageDialog(this, "修改成功");
dispose();
} else {
JOptionPane.showMessageDialog(this, "旧密码错误");
}
});
cancel.addActionListener(a -> dispose());
setVisible(true);

@ -1,11 +1,28 @@
package view;
import javax.swing.*;
import java.awt.*;
import java.awt.GridLayout;
import java.util.Properties;
import java.util.Random;
import jakarta.mail.*;
import jakarta.mail.internet.*;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
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 javax.swing.BorderFactory;
import jakarta.mail.Authenticator;
import jakarta.mail.Message;
import jakarta.mail.PasswordAuthentication;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import model.Login;
import model.LanguageSwitch;
@ -13,6 +30,7 @@ import model.LanguageSwitch;
* 使 QQ
*/
public class RegisterFrame extends JDialog {
private JTextField usernameField;
private JTextField emailField;
private JComboBox<String> levelBox;
@ -106,7 +124,7 @@ public class RegisterFrame extends JDialog {
Login.Level lv = LanguageSwitch.chineseToLevel(levelStr);
boolean ok = Login.register(u, pwd, lv, email);
if (!ok) {
JOptionPane.showMessageDialog(this, "用户名已存在,请换一个用户名");
JOptionPane.showMessageDialog(this, "用户名或邮箱已存在,请换一个用户名");
return;
}
JOptionPane.showMessageDialog(this, "注册成功,请用新用户登录");
@ -126,7 +144,6 @@ public class RegisterFrame extends JDialog {
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true"); // ← 开启 SSL
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(FROM_EMAIL, AUTH_CODE);

@ -1,21 +1,29 @@
package view;
import javax.swing.*;
import java.awt.*;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import model.Login;
/**
* 退
*/
public class ResultFrame extends JFrame {
public ResultFrame(Login.Account user, int score, int total, double percent) {
setTitle("成绩 - " + user.username);
setSize(360,220);
setSize(360, 220);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new GridLayout(5,1,6,6));
p.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
JPanel p = new JPanel(new GridLayout(5, 1, 6, 6));
p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
p.add(new JLabel("用户名: " + user.username));
p.add(new JLabel("得分: " + score + " / " + total));
p.add(new JLabel(String.format("百分比: %.2f%%", percent)));

Loading…
Cancel
Save