Compare commits
10 Commits
main
...
liuxingpen
| Author | SHA1 | Date |
|---|---|---|
|
|
f2c11f5e40 | 3 months ago |
|
|
2246773e9e | 3 months ago |
|
|
8dbfc46275 | 3 months ago |
|
|
e9c9f0229b | 3 months ago |
|
|
4e16ed40ad | 3 months ago |
|
|
1f3f90784d | 3 months ago |
|
|
8604fb743d | 3 months ago |
|
|
7d1cd35a80 | 3 months ago |
|
|
506b51f3d0 | 3 months ago |
|
|
023f7ae9d9 | 3 months ago |
@ -1,8 +0,0 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="22" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Y2.3.iml" filepath="$PROJECT_DIR$/Y2.3.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="f14627aa-6999-456f-900a-7580301e6797" name="更改" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 0
|
||||
}</component>
|
||||
<component name="ProjectId" id="33sLYnV1Yrm0gvXoLXrKZburltC" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Users/Administrator/Desktop/R/Y2.3/lib/javax.mail-1.6.2.jar",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"project.structure.last.edited": "模块",
|
||||
"project.structure.proportion": "0.0",
|
||||
"project.structure.side.proportion": "0.2",
|
||||
"vue.rearranger.settings.migration": "true",
|
||||
"应用程序.Main.executor": "Run"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-jdk-9f38398b9061-39b83d9b5494-intellij.indexing.shared.core-IU-241.17011.79" />
|
||||
<option value="bundled-js-predefined-1d06a55b98c1-0b3e54e931b4-JavaScript-IU-241.17011.79" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="默认任务">
|
||||
<changelist id="f14627aa-6999-456f-900a-7580301e6797" name="更改" comment="" />
|
||||
<created>1760100882302</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1760100882302</updated>
|
||||
<workItem from="1760100883365" duration="17000" />
|
||||
<workItem from="1760101061217" duration="763000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,20 +0,0 @@
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import controller.AppController;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("awt.useSystemAAFontSettings", "on");
|
||||
System.setProperty("swing.aatext", "true");
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(new FlatLightLaf());
|
||||
} catch (UnsupportedLookAndFeelException e) {
|
||||
System.err.println("无法设置UI主题: " + e.getMessage());
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(AppController::new);
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@ -1,10 +0,0 @@
|
||||
|
||||
smtp.host=smtp.qq.com
|
||||
smtp.port=465
|
||||
smtp.ssl.enable=true
|
||||
smtp.auth=true
|
||||
|
||||
|
||||
mail.sender.email=2631495488@qq.com
|
||||
|
||||
mail.sender.password=vdjvddhflexgdjfd
|
||||
@ -1,19 +0,0 @@
|
||||
package model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Question {
|
||||
private final String questionText;
|
||||
private final List<String> options;
|
||||
private final int correctOptionIndex;
|
||||
|
||||
public Question(String questionText, List<String> options, int correctOptionIndex) {
|
||||
this.questionText = questionText;
|
||||
this.options = options;
|
||||
this.correctOptionIndex = correctOptionIndex;
|
||||
}
|
||||
|
||||
public String getQuestionText() { return questionText; }
|
||||
public List<String> getOptions() { return options; }
|
||||
public int getCorrectOptionIndex() { return correctOptionIndex; }
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package model;
|
||||
|
||||
public enum UserType {
|
||||
PRIMARY("小学"),
|
||||
JUNIOR("初中"),
|
||||
SENIOR("高中");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
UserType(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
package service;
|
||||
|
||||
import model.User;
|
||||
import model.UserType;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
|
||||
public class UserManager {
|
||||
private final Path usersFile = Paths.get("data", "users.txt");
|
||||
private final Map<String, User> usersByEmail = new HashMap<>(); // 使用邮箱作为主键
|
||||
|
||||
public UserManager() {
|
||||
try {
|
||||
Files.createDirectories(usersFile.getParent());
|
||||
if (!Files.exists(usersFile)) Files.createFile(usersFile);
|
||||
loadUsers();
|
||||
} catch (IOException e) {
|
||||
System.err.println("初始化用户管理器时出错: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadUsers() throws IOException {
|
||||
List<String> lines = Files.readAllLines(usersFile, StandardCharsets.UTF_8);
|
||||
for (String line : lines) {
|
||||
User user = User.fromString(line);
|
||||
if (user != null) usersByEmail.put(user.getEmail(), user);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveUsers() {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(usersFile, StandardCharsets.UTF_8)) {
|
||||
for (User user : usersByEmail.values()) writer.write(user.toString() + "\n");
|
||||
} catch (IOException e) {
|
||||
System.err.println("保存用户时出错: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public User authenticate(String identifier, String password) {
|
||||
String hashedInput = hashPassword(password);
|
||||
|
||||
// 策略一:尝试将 identifier 作为邮箱进行快速查找
|
||||
User userByEmail = usersByEmail.get(identifier);
|
||||
if (userByEmail != null && userByEmail.getHashedPassword().equals(hashedInput)) {
|
||||
return userByEmail; // 邮箱匹配成功
|
||||
}
|
||||
|
||||
// 策略二:如果邮箱查找失败,则遍历所有用户,尝试匹配用户名
|
||||
for (User user : usersByEmail.values()) {
|
||||
if (user.getUsername() != null && user.getUsername().equals(identifier)) {
|
||||
if (user.getHashedPassword().equals(hashedInput)) {
|
||||
return user; // 用户名匹配成功
|
||||
}
|
||||
break; // 用户名是唯一的,找到后无需继续遍历
|
||||
}
|
||||
}
|
||||
|
||||
return null; // 认证失败
|
||||
}
|
||||
|
||||
public boolean emailExists(String email) {
|
||||
return usersByEmail.containsKey(email);
|
||||
}
|
||||
|
||||
public boolean usernameExists(String username) {
|
||||
if (username == null || username.trim().isEmpty()) return false;
|
||||
for (User user : usersByEmail.values()) {
|
||||
if (username.equals(user.getUsername())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void registerUser(String email, String username, String password, UserType type) {
|
||||
User newUser = new User(email, username, "", type);
|
||||
newUser.setHashedPassword(password);
|
||||
usersByEmail.put(email, newUser);
|
||||
saveUsers();
|
||||
}
|
||||
|
||||
public void updateUser(User user) {
|
||||
if (usersByEmail.containsKey(user.getEmail())) {
|
||||
usersByEmail.put(user.getEmail(), user);
|
||||
saveUsers();
|
||||
}
|
||||
}
|
||||
|
||||
private String hashPassword(String password) {
|
||||
return Integer.toString(password.hashCode());
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package service;
|
||||
|
||||
|
||||
public class ValidationService {
|
||||
public static boolean isValidEmail(String email) {
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
|
||||
return email.matches(emailRegex);
|
||||
}
|
||||
|
||||
public static boolean isPasswordStrong(String password) {
|
||||
if (password == null || password.length() < 6 || password.length() > 10) {
|
||||
return false;
|
||||
}
|
||||
boolean hasUpperCase = password.matches(".*[A-Z].*");
|
||||
boolean hasLowerCase = password.matches(".*[a-z].*");
|
||||
boolean hasDigit = password.matches(".*[0-9].*");
|
||||
|
||||
return hasUpperCase && hasLowerCase && hasDigit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
package service.generator;
|
||||
|
||||
public interface QuestionGenerator {
|
||||
String generateQuestion();
|
||||
}
|
||||
@ -1,140 +1,140 @@
|
||||
package view;
|
||||
|
||||
import model.Question;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import javax.swing.text.StyledDocument;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ExamView extends JPanel {
|
||||
private final JLabel progressLabel = new JLabel(" ", SwingConstants.CENTER);
|
||||
private final JProgressBar progressBar = new JProgressBar();
|
||||
private final JTextPane questionPane = new JTextPane();
|
||||
private final JRadioButton[] optionButtons = new JRadioButton[4];
|
||||
private final ButtonGroup optionsGroup = new ButtonGroup();
|
||||
|
||||
private final JButton prevButton = new JButton("上一题");
|
||||
private final JButton nextButton = new JButton("下一题");
|
||||
private final JButton finishButton = new JButton("交卷并评分");
|
||||
|
||||
public ExamView() {
|
||||
setLayout(new BorderLayout(20, 20));
|
||||
setBorder(new EmptyBorder(20, 40, 40, 40));
|
||||
|
||||
// --- 顶部进度区域 ---
|
||||
JPanel topPanel = new JPanel(new BorderLayout(0, 10));
|
||||
progressLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
|
||||
topPanel.add(progressLabel, BorderLayout.NORTH);
|
||||
|
||||
progressBar.setPreferredSize(new Dimension(1, 15));
|
||||
topPanel.add(progressBar, BorderLayout.CENTER);
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// --- 中间题目区域 ---
|
||||
questionPane.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 36));
|
||||
questionPane.setEditable(false);
|
||||
questionPane.setFocusable(false);
|
||||
questionPane.setOpaque(false);
|
||||
StyledDocument doc = questionPane.getStyledDocument();
|
||||
SimpleAttributeSet center = new SimpleAttributeSet();
|
||||
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
|
||||
doc.setParagraphAttributes(0, doc.getLength(), center, false);
|
||||
|
||||
JPanel questionWrapper = new JPanel(new BorderLayout());
|
||||
questionWrapper.setOpaque(false);
|
||||
questionWrapper.add(questionPane, BorderLayout.CENTER);
|
||||
add(questionWrapper, BorderLayout.CENTER);
|
||||
|
||||
// --- 底部选项和导航 ---
|
||||
JPanel southPanel = new JPanel(new BorderLayout(20, 30));
|
||||
|
||||
// --- 选项面板 ---
|
||||
JPanel optionsPanel = new JPanel(new GridBagLayout());
|
||||
optionsPanel.setOpaque(false);
|
||||
GridBagConstraints gbcOpt = new GridBagConstraints();
|
||||
gbcOpt.insets = new Insets(15, 80, 15, 80); // 调整选项的内外边距
|
||||
gbcOpt.anchor = GridBagConstraints.WEST;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i] = new JRadioButton();
|
||||
optionButtons[i].setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 20));
|
||||
optionButtons[i].setOpaque(false);
|
||||
optionsGroup.add(optionButtons[i]);
|
||||
}
|
||||
|
||||
|
||||
gbcOpt.gridx = 0; gbcOpt.gridy = 0; optionsPanel.add(optionButtons[0], gbcOpt); // 位置 A
|
||||
gbcOpt.gridx = 1; gbcOpt.gridy = 0; optionsPanel.add(optionButtons[1], gbcOpt); // 位置 B
|
||||
gbcOpt.gridx = 0; gbcOpt.gridy = 1; optionsPanel.add(optionButtons[2], gbcOpt); // 位置 C
|
||||
gbcOpt.gridx = 1; gbcOpt.gridy = 1; optionsPanel.add(optionButtons[3], gbcOpt); // 位置 D
|
||||
|
||||
JPanel optionsWrapper = new JPanel(new GridBagLayout());
|
||||
optionsWrapper.setOpaque(false);
|
||||
optionsWrapper.add(optionsPanel);
|
||||
southPanel.add(optionsWrapper, BorderLayout.CENTER);
|
||||
|
||||
// --- 导航按钮面板 ---
|
||||
JPanel navigationPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 30, 0));
|
||||
prevButton.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
nextButton.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
finishButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
|
||||
finishButton.putClientProperty("JButton.buttonType", "roundRect");
|
||||
|
||||
navigationPanel.add(prevButton);
|
||||
navigationPanel.add(finishButton);
|
||||
navigationPanel.add(nextButton);
|
||||
southPanel.add(navigationPanel, BorderLayout.SOUTH);
|
||||
|
||||
add(southPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
public void displayQuestion(Question q, int qNum, int total, int savedAnswerIndex) {
|
||||
progressLabel.setText(String.format("第 %d 题 / 共 %d 题", qNum, total));
|
||||
progressBar.setValue(qNum);
|
||||
questionPane.setText(q.getQuestionText());
|
||||
|
||||
StyledDocument doc = questionPane.getStyledDocument();
|
||||
SimpleAttributeSet center = new SimpleAttributeSet();
|
||||
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
|
||||
doc.setParagraphAttributes(0, doc.getLength(), center, false);
|
||||
|
||||
optionsGroup.clearSelection();
|
||||
List<String> options = q.getOptions();
|
||||
|
||||
char optionLabel = 'A';
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i].setText(optionLabel + ". " + options.get(i));
|
||||
optionLabel++;
|
||||
}
|
||||
|
||||
if (savedAnswerIndex != -1) {
|
||||
optionButtons[savedAnswerIndex].setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setupForExam(int totalQuestions) {
|
||||
progressBar.setMinimum(1);
|
||||
progressBar.setMaximum(totalQuestions);
|
||||
}
|
||||
|
||||
public void updateNavigationButtons(int qIndex, int total) {
|
||||
prevButton.setEnabled(qIndex > 0);
|
||||
nextButton.setEnabled(qIndex < total - 1);
|
||||
nextButton.setText(qIndex == total - 2 ? "最后一题" : "下一题");
|
||||
}
|
||||
|
||||
public int getSelectedIndex() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (optionButtons[i].isSelected()) { return i; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public JButton getPrevButton() { return prevButton; }
|
||||
public JButton getNextButton() { return nextButton; }
|
||||
public JButton getFinishButton() { return finishButton; }
|
||||
package view;
|
||||
|
||||
import model.Question;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import javax.swing.text.StyledDocument;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ExamView extends JPanel {
|
||||
private final JLabel progressLabel = new JLabel(" ", SwingConstants.CENTER);
|
||||
private final JProgressBar progressBar = new JProgressBar();
|
||||
private final JTextPane questionPane = new JTextPane();
|
||||
private final JRadioButton[] optionButtons = new JRadioButton[4];
|
||||
private final ButtonGroup optionsGroup = new ButtonGroup();
|
||||
|
||||
private final JButton prevButton = new JButton("上一题");
|
||||
private final JButton nextButton = new JButton("下一题");
|
||||
private final JButton finishButton = new JButton("交卷并评分");
|
||||
|
||||
public ExamView() {
|
||||
setLayout(new BorderLayout(20, 20));
|
||||
setBorder(new EmptyBorder(20, 40, 40, 40));
|
||||
|
||||
// --- 顶部进度区域 ---
|
||||
JPanel topPanel = new JPanel(new BorderLayout(0, 10));
|
||||
progressLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
|
||||
topPanel.add(progressLabel, BorderLayout.NORTH);
|
||||
|
||||
progressBar.setPreferredSize(new Dimension(1, 15));
|
||||
topPanel.add(progressBar, BorderLayout.CENTER);
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// --- 中间题目区域 ---
|
||||
questionPane.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 36));
|
||||
questionPane.setEditable(false);
|
||||
questionPane.setFocusable(false);
|
||||
questionPane.setOpaque(false);
|
||||
StyledDocument doc = questionPane.getStyledDocument();
|
||||
SimpleAttributeSet center = new SimpleAttributeSet();
|
||||
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
|
||||
doc.setParagraphAttributes(0, doc.getLength(), center, false);
|
||||
|
||||
JPanel questionWrapper = new JPanel(new BorderLayout());
|
||||
questionWrapper.setOpaque(false);
|
||||
questionWrapper.add(questionPane, BorderLayout.CENTER);
|
||||
add(questionWrapper, BorderLayout.CENTER);
|
||||
|
||||
// --- 底部选项和导航 ---
|
||||
JPanel southPanel = new JPanel(new BorderLayout(20, 30));
|
||||
|
||||
// --- 选项面板 ---
|
||||
JPanel optionsPanel = new JPanel(new GridBagLayout());
|
||||
optionsPanel.setOpaque(false);
|
||||
GridBagConstraints gbcOpt = new GridBagConstraints();
|
||||
gbcOpt.insets = new Insets(15, 80, 15, 80); // 调整选项的内外边距
|
||||
gbcOpt.anchor = GridBagConstraints.WEST;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i] = new JRadioButton();
|
||||
optionButtons[i].setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 20));
|
||||
optionButtons[i].setOpaque(false);
|
||||
optionsGroup.add(optionButtons[i]);
|
||||
}
|
||||
|
||||
|
||||
gbcOpt.gridx = 0; gbcOpt.gridy = 0; optionsPanel.add(optionButtons[0], gbcOpt); // 位置 A
|
||||
gbcOpt.gridx = 1; gbcOpt.gridy = 0; optionsPanel.add(optionButtons[1], gbcOpt); // 位置 B
|
||||
gbcOpt.gridx = 0; gbcOpt.gridy = 1; optionsPanel.add(optionButtons[2], gbcOpt); // 位置 C
|
||||
gbcOpt.gridx = 1; gbcOpt.gridy = 1; optionsPanel.add(optionButtons[3], gbcOpt); // 位置 D
|
||||
|
||||
JPanel optionsWrapper = new JPanel(new GridBagLayout());
|
||||
optionsWrapper.setOpaque(false);
|
||||
optionsWrapper.add(optionsPanel);
|
||||
southPanel.add(optionsWrapper, BorderLayout.CENTER);
|
||||
|
||||
// --- 导航按钮面板 ---
|
||||
JPanel navigationPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 30, 0));
|
||||
prevButton.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
nextButton.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
finishButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
|
||||
finishButton.putClientProperty("JButton.buttonType", "roundRect");
|
||||
|
||||
navigationPanel.add(prevButton);
|
||||
navigationPanel.add(finishButton);
|
||||
navigationPanel.add(nextButton);
|
||||
southPanel.add(navigationPanel, BorderLayout.SOUTH);
|
||||
|
||||
add(southPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
public void displayQuestion(Question q, int qNum, int total, int savedAnswerIndex) {
|
||||
progressLabel.setText(String.format("第 %d 题 / 共 %d 题", qNum, total));
|
||||
progressBar.setValue(qNum);
|
||||
questionPane.setText(q.getQuestionText());
|
||||
|
||||
StyledDocument doc = questionPane.getStyledDocument();
|
||||
SimpleAttributeSet center = new SimpleAttributeSet();
|
||||
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
|
||||
doc.setParagraphAttributes(0, doc.getLength(), center, false);
|
||||
|
||||
optionsGroup.clearSelection();
|
||||
List<String> options = q.getOptions();
|
||||
|
||||
char optionLabel = 'A';
|
||||
for (int i = 0; i < 4; i++) {
|
||||
optionButtons[i].setText(optionLabel + ". " + options.get(i));
|
||||
optionLabel++;
|
||||
}
|
||||
|
||||
if (savedAnswerIndex != -1) {
|
||||
optionButtons[savedAnswerIndex].setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setupForExam(int totalQuestions) {
|
||||
progressBar.setMinimum(1);
|
||||
progressBar.setMaximum(totalQuestions);
|
||||
}
|
||||
|
||||
public void updateNavigationButtons(int qIndex, int total) {
|
||||
prevButton.setEnabled(qIndex > 0);
|
||||
nextButton.setEnabled(qIndex < total - 1);
|
||||
nextButton.setText(qIndex == total - 2 ? "最后一题" : "下一题");
|
||||
}
|
||||
|
||||
public int getSelectedIndex() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (optionButtons[i].isSelected()) { return i; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public JButton getPrevButton() { return prevButton; }
|
||||
public JButton getNextButton() { return nextButton; }
|
||||
public JButton getFinishButton() { return finishButton; }
|
||||
}
|
||||
@ -1,64 +1,64 @@
|
||||
package view;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class LoginView extends JPanel {
|
||||
private final JTextField identifierField = new JTextField(20);
|
||||
private final JPasswordField passwordField = new JPasswordField(20);
|
||||
private final JButton loginButton = new JButton("登 录");
|
||||
private final JButton registerButton = new JButton("还没有账号?立即注册");
|
||||
|
||||
public LoginView() {
|
||||
setLayout(new GridBagLayout());
|
||||
|
||||
JPanel formPanel = new JPanel(new GridBagLayout());
|
||||
formPanel.setBorder(BorderFactory.createEmptyBorder(30, 40, 30, 40));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(10, 10, 10, 10);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel titleLabel = new JLabel("欢迎回来", SwingConstants.CENTER);
|
||||
titleLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 28));
|
||||
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
||||
formPanel.add(titleLabel, gbc);
|
||||
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridx = 0; gbc.gridy = 1; addLabel(formPanel, "邮箱 / 用户名:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 1; addComponent(formPanel, identifierField, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 2; addLabel(formPanel, "密码:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 2; addComponent(formPanel, passwordField, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2;
|
||||
loginButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
|
||||
addComponent(formPanel, loginButton, gbc);
|
||||
|
||||
gbc.gridy = 4;
|
||||
|
||||
registerButton.putClientProperty("FlatLaf.style", "buttonType: borderless");
|
||||
addComponent(formPanel, registerButton, gbc);
|
||||
|
||||
add(formPanel);
|
||||
}
|
||||
|
||||
private void addLabel(JPanel panel, String text, GridBagConstraints gbc) {
|
||||
JLabel label = new JLabel(text);
|
||||
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(label, gbc);
|
||||
}
|
||||
|
||||
private void addComponent(JPanel panel, JComponent comp, GridBagConstraints gbc) {
|
||||
comp.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(comp, gbc);
|
||||
}
|
||||
|
||||
public String getIdentifier() { return identifierField.getText().trim(); }
|
||||
public String getPassword() { return new String(passwordField.getPassword()); }
|
||||
public JButton getLoginButton() { return loginButton; }
|
||||
public JButton getRegisterButton() { return registerButton; }
|
||||
public void clearFields() {
|
||||
identifierField.setText("");
|
||||
passwordField.setText("");
|
||||
}
|
||||
package view;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class LoginView extends JPanel {
|
||||
private final JTextField identifierField = new JTextField(20);
|
||||
private final JPasswordField passwordField = new JPasswordField(20);
|
||||
private final JButton loginButton = new JButton("登 录");
|
||||
private final JButton registerButton = new JButton("还没有账号?立即注册");
|
||||
|
||||
public LoginView() {
|
||||
setLayout(new GridBagLayout());
|
||||
|
||||
JPanel formPanel = new JPanel(new GridBagLayout());
|
||||
formPanel.setBorder(BorderFactory.createEmptyBorder(30, 40, 30, 40));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(10, 10, 10, 10);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel titleLabel = new JLabel("欢迎回来", SwingConstants.CENTER);
|
||||
titleLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 28));
|
||||
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
||||
formPanel.add(titleLabel, gbc);
|
||||
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridx = 0; gbc.gridy = 1; addLabel(formPanel, "邮箱 / 用户名:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 1; addComponent(formPanel, identifierField, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 2; addLabel(formPanel, "密码:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 2; addComponent(formPanel, passwordField, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2;
|
||||
loginButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
|
||||
addComponent(formPanel, loginButton, gbc);
|
||||
|
||||
gbc.gridy = 4;
|
||||
|
||||
registerButton.putClientProperty("FlatLaf.style", "buttonType: borderless");
|
||||
addComponent(formPanel, registerButton, gbc);
|
||||
|
||||
add(formPanel);
|
||||
}
|
||||
|
||||
private void addLabel(JPanel panel, String text, GridBagConstraints gbc) {
|
||||
JLabel label = new JLabel(text);
|
||||
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(label, gbc);
|
||||
}
|
||||
|
||||
private void addComponent(JPanel panel, JComponent comp, GridBagConstraints gbc) {
|
||||
comp.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(comp, gbc);
|
||||
}
|
||||
|
||||
public String getIdentifier() { return identifierField.getText().trim(); }
|
||||
public String getPassword() { return new String(passwordField.getPassword()); }
|
||||
public JButton getLoginButton() { return loginButton; }
|
||||
public JButton getRegisterButton() { return registerButton; }
|
||||
public void clearFields() {
|
||||
identifierField.setText("");
|
||||
passwordField.setText("");
|
||||
}
|
||||
}
|
||||
@ -1,27 +1,27 @@
|
||||
package view;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MainFrame extends JFrame {
|
||||
private final CardLayout cardLayout = new CardLayout();
|
||||
private final JPanel mainPanel = new JPanel(cardLayout);
|
||||
|
||||
public MainFrame() {
|
||||
super("数学学习软件");
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setSize(800, 600);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
add(mainPanel);
|
||||
}
|
||||
|
||||
public void addPanel(JPanel panel, String name) {
|
||||
mainPanel.add(panel, name);
|
||||
}
|
||||
|
||||
public void showPanel(String name) {
|
||||
cardLayout.show(mainPanel, name);
|
||||
setVisible(true);
|
||||
}
|
||||
package view;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MainFrame extends JFrame {
|
||||
private final CardLayout cardLayout = new CardLayout();
|
||||
private final JPanel mainPanel = new JPanel(cardLayout);
|
||||
|
||||
public MainFrame() {
|
||||
super("数学学习软件");
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setSize(800, 600);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
add(mainPanel);
|
||||
}
|
||||
|
||||
public void addPanel(JPanel panel, String name) {
|
||||
mainPanel.add(panel, name);
|
||||
}
|
||||
|
||||
public void showPanel(String name) {
|
||||
cardLayout.show(mainPanel, name);
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
@ -1,72 +1,72 @@
|
||||
package view;
|
||||
|
||||
import model.UserType;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MainMenuView extends JPanel {
|
||||
private final JLabel welcomeLabel = new JLabel("", SwingConstants.CENTER);
|
||||
private final JComboBox<String> typeComboBox = new JComboBox<>();
|
||||
private final JSpinner countSpinner = new JSpinner(new SpinnerNumberModel(10, 10, 30, 1));
|
||||
private final JButton startExamButton = new JButton("开始答题");
|
||||
private final JButton changePasswordButton = new JButton("修改密码");
|
||||
private final JButton logoutButton = new JButton("退出登录");
|
||||
|
||||
public MainMenuView() {
|
||||
setLayout(new BorderLayout(20, 20));
|
||||
setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40));
|
||||
|
||||
welcomeLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 28));
|
||||
add(welcomeLabel, BorderLayout.NORTH);
|
||||
|
||||
JPanel centerPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(15, 10, 15, 10);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 0; addLabel(centerPanel, "选择题目难度:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 0; addComponent(centerPanel, typeComboBox, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 1; addLabel(centerPanel, "选择题目数量:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 1; addComponent(centerPanel, countSpinner, gbc);
|
||||
|
||||
startExamButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
|
||||
|
||||
startExamButton.putClientProperty("JButton.buttonType", "roundRect");
|
||||
gbc.gridy = 2; gbc.gridwidth = 2; gbc.insets = new Insets(30, 10, 15, 10);
|
||||
addComponent(centerPanel, startExamButton, gbc);
|
||||
add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 20, 0));
|
||||
southPanel.add(changePasswordButton);
|
||||
southPanel.add(logoutButton);
|
||||
add(southPanel, BorderLayout.SOUTH);
|
||||
|
||||
for (UserType type : UserType.values()) {
|
||||
typeComboBox.addItem(type.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
private void addLabel(JPanel panel, String text, GridBagConstraints gbc) {
|
||||
JLabel label = new JLabel(text);
|
||||
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(label, gbc);
|
||||
}
|
||||
|
||||
private void addComponent(JPanel panel, JComponent comp, GridBagConstraints gbc) {
|
||||
comp.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(comp, gbc);
|
||||
}
|
||||
|
||||
public void setWelcomeMessage(String message) { welcomeLabel.setText(message); }
|
||||
public UserType getSelectedType() {
|
||||
String displayName = (String) typeComboBox.getSelectedItem();
|
||||
for (UserType type : UserType.values()) {
|
||||
if (type.getDisplayName().equals(displayName)) return type;
|
||||
}
|
||||
return UserType.PRIMARY;
|
||||
}
|
||||
public int getQuestionCount() { return (int) countSpinner.getValue(); }
|
||||
public JButton getStartExamButton() { return startExamButton; }
|
||||
public JButton getChangePasswordButton() { return changePasswordButton; }
|
||||
public JButton getLogoutButton() { return logoutButton; }
|
||||
package view;
|
||||
|
||||
import model.UserType;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MainMenuView extends JPanel {
|
||||
private final JLabel welcomeLabel = new JLabel("", SwingConstants.CENTER);
|
||||
private final JComboBox<String> typeComboBox = new JComboBox<>();
|
||||
private final JSpinner countSpinner = new JSpinner(new SpinnerNumberModel(10, 10, 30, 1));
|
||||
private final JButton startExamButton = new JButton("开始答题");
|
||||
private final JButton changePasswordButton = new JButton("修改密码");
|
||||
private final JButton logoutButton = new JButton("退出登录");
|
||||
|
||||
public MainMenuView() {
|
||||
setLayout(new BorderLayout(20, 20));
|
||||
setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40));
|
||||
|
||||
welcomeLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 28));
|
||||
add(welcomeLabel, BorderLayout.NORTH);
|
||||
|
||||
JPanel centerPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(15, 10, 15, 10);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 0; addLabel(centerPanel, "选择题目难度:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 0; addComponent(centerPanel, typeComboBox, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 1; addLabel(centerPanel, "选择题目数量:", gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 1; addComponent(centerPanel, countSpinner, gbc);
|
||||
|
||||
startExamButton.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
|
||||
|
||||
startExamButton.putClientProperty("JButton.buttonType", "roundRect");
|
||||
gbc.gridy = 2; gbc.gridwidth = 2; gbc.insets = new Insets(30, 10, 15, 10);
|
||||
addComponent(centerPanel, startExamButton, gbc);
|
||||
add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 20, 0));
|
||||
southPanel.add(changePasswordButton);
|
||||
southPanel.add(logoutButton);
|
||||
add(southPanel, BorderLayout.SOUTH);
|
||||
|
||||
for (UserType type : UserType.values()) {
|
||||
typeComboBox.addItem(type.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
private void addLabel(JPanel panel, String text, GridBagConstraints gbc) {
|
||||
JLabel label = new JLabel(text);
|
||||
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(label, gbc);
|
||||
}
|
||||
|
||||
private void addComponent(JPanel panel, JComponent comp, GridBagConstraints gbc) {
|
||||
comp.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
|
||||
panel.add(comp, gbc);
|
||||
}
|
||||
|
||||
public void setWelcomeMessage(String message) { welcomeLabel.setText(message); }
|
||||
public UserType getSelectedType() {
|
||||
String displayName = (String) typeComboBox.getSelectedItem();
|
||||
for (UserType type : UserType.values()) {
|
||||
if (type.getDisplayName().equals(displayName)) return type;
|
||||
}
|
||||
return UserType.PRIMARY;
|
||||
}
|
||||
public int getQuestionCount() { return (int) countSpinner.getValue(); }
|
||||
public JButton getStartExamButton() { return startExamButton; }
|
||||
public JButton getChangePasswordButton() { return changePasswordButton; }
|
||||
public JButton getLogoutButton() { return logoutButton; }
|
||||
}
|
||||
Loading…
Reference in new issue