新增frontend

develop
jiazhuo 7 months ago
parent 537a674da0
commit 47f3e95801

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@
{
"name": "math-learning-app",
"version": "1.0.0",
"description": "小初高数学学习软件",
"main": "src/main.js",
"scripts": {
"start": "electron .",
"dev": "electron . --dev",
"build": "electron-builder"
},
"devDependencies": {
"electron": "^22.0.0",
"electron-builder": "^24.0.0"
},
"build": {
"appId": "com.mathlearning.app",
"productName": "数学学习系统",
"directories": {
"output": "dist"
},
"files": [
"src/**/*",
"styles/**/*",
"assets/**/*"
],
"win": {
"target": "nsis",
"icon": "assets/icon.ico"
}
},
"dependencies": {
"node-fetch": "^2.7.0"
}
}

@ -0,0 +1,100 @@
const fetch = require('node-fetch');
const { app, BrowserWindow, Menu, ipcMain } = require('electron');
const path = require('path');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'), // 确保路径正确
webSecurity: false // 开发时可以关闭避免CORS问题
},
icon: path.join(__dirname, 'assets/icon.png') // 可选
});
// 加载登录页面
mainWindow.loadFile(path.join(__dirname, 'renderer/login.html'));
// 开发时打开开发者工具
mainWindow.webContents.openDevTools();
}
function createMenu() {
const template = [
{
label: '文件',
submenu: [
{
label: '退出',
click: () => app.quit()
}
]
},
{
label: '查看',
submenu: [
{ role: 'reload' },
{ role: 'toggleDevTools' }
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
// API 基础URL
const API_BASE_URL = 'http://localhost:8080/api';
// 处理API请求
ipcMain.handle('api-request', async (event, { endpoint, method = 'POST', data = {} }) => {
try {
const url = `${API_BASE_URL}${endpoint}`;
const formData = new URLSearchParams();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
});
const result = await response.json();
return result;
} catch (error) {
return {
success: false,
message: `网络错误: ${error.message}`,
data: null
};
}
});
// 页面导航
ipcMain.handle('navigate-to', (event, page) => {
mainWindow.loadFile(path.join(__dirname, `renderer/${page}.html`));
});
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

@ -0,0 +1,129 @@
const { contextBridge, ipcRenderer } = require('electron');
// 统一的会话管理器
const SessionManager = {
// 获取当前用户会话(强制从存储读取)
getCurrentUser() {
try {
const session = localStorage.getItem('userSession');
return session ? JSON.parse(session) : null;
} catch (error) {
console.error('获取用户会话失败:', error);
return null;
}
},
// 获取当前考试会话
getCurrentExam() {
try {
const exam = localStorage.getItem('currentExam');
return exam ? JSON.parse(exam) : null;
} catch (error) {
console.error('获取考试会话失败:', error);
return null;
}
},
// 清除所有会话
clearAllSessions() {
localStorage.removeItem('userSession');
localStorage.removeItem('currentExam');
localStorage.removeItem('examResult');
console.log('所有会话已清除');
},
// 验证会话是否有效
validateUserSession() {
const user = this.getCurrentUser();
if (!user || !user.sessionId) {
return false;
}
// 可以添加更多验证逻辑,比如检查过期时间等
return true;
},
// 刷新用户会话(在修改密码等操作后)
refreshUserSession(updatedUserData) {
if (updatedUserData) {
localStorage.setItem('userSession', JSON.stringify(updatedUserData));
}
}
};
// 暴露安全的API给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
// 页面导航
navigateTo: (page) => ipcRenderer.invoke('navigate-to', page),
// 存储会话数据
setSession: (key, value) => {
localStorage.setItem(key, JSON.stringify(value));
},
getSession: (key) => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
},
removeSession: (key) => {
localStorage.removeItem(key);
},
// 新增:获取当前会话状态
getSessionState: () => {
return SessionManager.validateSession();
},
// 新增:强制清除所有会话
clearAllSessions: () => {
SessionManager.clearAllSessions();
},
// 新增会话验证方法
validateSession: () => {
return SessionManager.validateUserSession();
},
// 新增会话刷新方法
refreshSession: (userData) => {
SessionManager.refreshUserSession(userData);
}
});
// 直接在渲染进程中暴露 API 调用函数
contextBridge.exposeInMainWorld('api', {
request: async (endpoint, method = 'POST', data = {}) => {
try {
console.log('API Request:', endpoint, method, data);
const url = `http://localhost:8080/api${endpoint}`;
const formData = new URLSearchParams();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
});
const result = await response.json();
console.log('API Response:', result);
return result;
} catch (error) {
console.error('API Error:', error);
return {
success: false,
message: `网络错误: ${error.message}`,
data: null
};
}
}
});
console.log('Preload script loaded successfully');

@ -0,0 +1,119 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数学试卷自动生成系统 - 登录</title>
<link rel="stylesheet" href="../style/common.css">
<style>
.login-container {
max-width: 400px;
margin: 100px auto;
padding: 40px;
background: white;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 20px;
}
input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
}
button {
width: 100%;
padding: 12px;
background: #007acc;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
button:hover {
background: #005a9e;
}
.link {
color: #007acc;
cursor: pointer;
text-align: center;
display: block;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="login-container">
<h2>数学试卷自动生成系统</h2>
<form id="loginForm">
<div class="form-group">
<input type="text" id="username" placeholder="用户名" required>
</div>
<div class="form-group">
<input type="password" id="password" placeholder="密码" required>
</div>
<button type="submit">登录</button>
</form>
<span class="link" id="registerLink">没有账号?立即注册</span>
<div id="message" class="message"></div>
</div>
<script>
// 页面加载完成后自动聚焦到用户名输入框
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
const usernameInput = document.getElementById('username');
if (usernameInput) {
// 强制触发重绘
usernameInput.style.display = 'none';
usernameInput.offsetHeight; // 触发回流
usernameInput.style.display = '';
// 设置焦点
usernameInput.focus();
}
}, 100);
});
document.getElementById('loginForm').addEventListener('submit', async (e) => {
// 原有登录逻辑保持不变...
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const messageEl = document.getElementById('message');
const result = await window.api.request('/login', 'POST', {
username, password
});
if (result.success) {
window.electronAPI.setSession('userSession', {
sessionId: result.data.sessionId,
username: result.data.username,
userType: result.data.userType
});
await window.electronAPI.navigateTo('main');
} else {
messageEl.textContent = result.message;
messageEl.className = 'message error';
}
});
document.getElementById('registerLink').addEventListener('click', async () => {
await window.electronAPI.navigateTo('register');
});
</script>
</body>
</html>

@ -0,0 +1,464 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数学学习系统 - 主页</title>
<link rel="stylesheet" href="../style/common.css">
<style>
.main-container {
max-width: 600px;
margin: 50px auto;
padding: 40px;
background: white;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.user-info {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-bottom: 30px;
}
.btn-group {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
button {
flex: 1;
padding: 15px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
.primary {
background: #007acc;
color: white;
}
.success {
background: #28a745;
color: white;
}
.warning {
background: #ffc107;
color: black;
}
.danger {
background: #dc3545;
color: white;
}
button:hover {
opacity: 0.9;
}
.exam-settings {
margin-top: 30px;
}
input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
margin-bottom: 15px;
}
</style>
</head>
<body>
<div class="main-container">
<h2>数学学习系统</h2>
<div class="user-info" id="userInfo">
<!-- 用户信息将通过JavaScript填充 -->
</div>
<div class="btn-group">
<button class="primary" data-type="小学">小学题目</button>
<button class="success" data-type="初中">初中题目</button>
<button class="warning" data-type="高中">高中题目</button>
</div>
<div class="exam-settings" id="examSettings" style="display: none;">
<h3>生成 <span id="selectedType"></span> 题目</h3>
<input type="number" id="questionCount" placeholder="题目数量 (10-30)" min="10" max="30">
<button class="primary" id="startExam">开始答题</button>
</div>
<div style="margin-top: 30px;">
<button class="danger" id="logoutBtn">退出登录</button>
<button class="warning" id="changePasswordBtn">修改密码</button>
</div>
</div>
<script>
let currentUser = null;
let selectedType = null;
document.addEventListener('DOMContentLoaded', async () => {
// 获取当前用户信息
currentUser = window.electronAPI.getSession('userSession');
if (!currentUser) {
await window.electronAPI.navigateTo('login');
return;
}
// 显示用户信息
document.getElementById('userInfo').innerHTML = `
<strong>用户名:</strong> ${currentUser.username}<br>
<strong>用户类型:</strong> ${currentUser.userType}
`;
// 绑定题目类型按钮事件
document.querySelectorAll('.btn-group button').forEach(btn => {
btn.addEventListener('click', (e) => {
selectedType = e.target.dataset.type;
document.getElementById('selectedType').textContent = selectedType;
document.getElementById('examSettings').style.display = 'block';
});
});
// 绑定开始答题按钮事件
const startExamBtn = document.getElementById('startExam');
if (startExamBtn) {
startExamBtn.addEventListener('click', startExam);
}
// 绑定退出登录按钮
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', logout);
}
// 绑定修改密码按钮
const changePasswordBtn = document.getElementById('changePasswordBtn');
if (changePasswordBtn) {
changePasswordBtn.addEventListener('click', showChangePasswordModal);
}
});
async function startExam() {
console.log('=== 开始答题流程开始 ===');
const questionCount = parseInt(document.getElementById('questionCount').value);
if (!selectedType) {
alert('请先选择题目类型');
return;
}
if (isNaN(questionCount) || questionCount < 10 || questionCount > 30) {
alert('题目数量应在10-30之间');
return;
}
try {
// 重新获取最新用户会话
const latestUser = window.electronAPI.getSession('userSession');
console.log('当前用户会话:', latestUser);
if (!latestUser || !latestUser.sessionId) {
alert('用户会话已过期,请重新登录');
await window.electronAPI.navigateTo('login');
return;
}
console.log('开始生成试卷请求:', {
sessionId: latestUser.sessionId,
userType: selectedType,
questionCount: questionCount
});
// 显示加载状态
const startExamBtn = document.getElementById('startExam');
const originalText = startExamBtn.textContent;
startExamBtn.textContent = '生成中...';
startExamBtn.disabled = true;
console.log('发送生成试卷请求...');
// 添加超时控制
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
try {
const result = await window.api.request('/generate-exam', 'POST', {
sessionId: latestUser.sessionId,
userType: selectedType,
questionCount: questionCount
});
clearTimeout(timeoutId);
console.log('生成试卷API响应:', result);
if (result.success) {
// 保存考试信息 - 确保包含所有必要字段
const examData = {
examId: result.data.examId,
totalQuestions: result.data.totalQuestions,
currentQuestion: result.data.currentQuestion || 0,
userType: selectedType,
// 保存第一题的详细信息,确保跳转后能立即显示
firstQuestion: {
question: result.data.question,
options: result.data.options
}
};
console.log('保存考试数据:', examData);
window.electronAPI.setSession('currentExam', examData);
// 确保数据保存完成后再跳转
setTimeout(async () => {
console.log('考试数据已保存,准备跳转到答题页面');
await window.electronAPI.navigateTo('quiz');
}, 100);
} else {
alert('生成试卷失败: ' + result.message);
// 如果是会话问题,清除会话
if (result.message.includes('会话') || result.message.includes('登录')) {
window.electronAPI.removeSession('userSession');
await window.electronAPI.navigateTo('login');
}
}
} catch (fetchError) {
clearTimeout(timeoutId);
throw fetchError;
}
} catch (error) {
console.error('生成试卷错误:', error);
if (error.name === 'AbortError') {
alert('请求超时!请检查:\n1. 后端Java程序是否运行\n2. 网络连接是否正常\n3. 端口8080是否被占用');
} else {
alert('生成试卷时发生错误: ' + error.message);
}
} finally {
// 恢复按钮状态
const startExamBtn = document.getElementById('startExam');
if (startExamBtn) {
startExamBtn.textContent = '开始答题';
startExamBtn.disabled = false;
}
}
console.log('=== 开始答题流程结束 ===');
}
// 网络连接测试函数
async function testBackendConnectivity() {
try {
console.log('测试后端连接...');
// 使用更简单的请求测试连接
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);
const response = await fetch('http://localhost:8080/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'username=test&password=test',
signal: controller.signal
});
clearTimeout(timeoutId);
return {
connected: true,
status: response.status
};
} catch (error) {
console.error('网络连接测试失败:', error);
return {
connected: false,
error: error.message
};
}
}
async function logout() {
if (confirm('确定要退出登录吗?')) {
try {
// 获取最新用户会话
const latestUser = window.electronAPI.getSession('userSession');
console.log('退出登录,用户信息:', latestUser);
if (latestUser && latestUser.sessionId) {
console.log('发送退出登录请求sessionId:', latestUser.sessionId);
const result = await window.api.request('/logout', 'POST', {
sessionId: latestUser.sessionId
});
console.log('退出登录响应:', result);
} else {
console.log('没有有效的用户会话,直接清除本地数据');
}
} catch (error) {
console.error('退出登录API错误:', error);
// 即使API失败也继续清除本地数据
} finally {
// 清除所有本地存储
window.electronAPI.removeSession('userSession');
window.electronAPI.removeSession('currentExam');
window.electronAPI.removeSession('examResult');
console.log('本地会话已清除,跳转到登录页面');
// 直接跳转
await window.electronAPI.navigateTo('login');
}
}
}
// 显示修改密码模态框
function showChangePasswordModal() {
// 创建修改密码模态框
const modalHtml = `
<div id="passwordModal" style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:flex;justify-content:center;align-items:center;z-index:1000;">
<div style="background:white;padding:30px;border-radius:10px;width:400px;" id="passwordModalContent">
<h3>修改密码</h3>
<div class="form-group">
<input type="password" id="oldPassword" placeholder="原密码" style="width:100%;padding:10px;margin:10px 0;">
</div>
<div class="form-group">
<input type="password" id="newPassword" placeholder="新密码" style="width:100%;padding:10px;margin:10px 0;">
</div>
<div class="form-group">
<input type="password" id="confirmPassword" placeholder="确认新密码" style="width:100%;padding:10px;margin:10px 0;">
</div>
<div style="display:flex;gap:10px;margin-top:20px;">
<button id="confirmChangePassword" style="flex:1;padding:10px;background:#007acc;color:white;border:none;border-radius:5px;">确认修改</button>
<button id="cancelChangePassword" style="flex:1;padding:10px;background:#6c757d;color:white;border:none;border-radius:5px;">取消</button>
</div>
<div id="passwordMessage" style="margin-top:10px;"></div>
</div>
</div>
`;
// 添加模态框到页面
document.body.insertAdjacentHTML('beforeend', modalHtml);
// 绑定模态框事件 - 阻止内容区域点击事件冒泡
const modalContent = document.getElementById('passwordModalContent');
modalContent.addEventListener('click', function (e) {
e.stopPropagation();
});
// 绑定按钮事件
document.getElementById('confirmChangePassword').addEventListener('click', changePassword);
document.getElementById('cancelChangePassword').addEventListener('click', closePasswordModal);
// 点击模态框背景关闭
document.getElementById('passwordModal').addEventListener('click', function (e) {
if (e.target === this) {
closePasswordModal();
}
});
// 自动聚焦到第一个输入框
setTimeout(() => {
document.getElementById('oldPassword').focus();
}, 100);
}
// 关闭修改密码模态框
function closePasswordModal() {
const modal = document.getElementById('passwordModal');
if (modal) {
modal.remove();
}
}
async function changePassword() {
const oldPassword = document.getElementById('oldPassword').value;
const newPassword = document.getElementById('newPassword').value;
const confirmPassword = document.getElementById('confirmPassword').value;
const messageEl = document.getElementById('passwordMessage');
// 清空之前的消息
messageEl.textContent = '';
messageEl.style.color = '';
if (!oldPassword || !newPassword || !confirmPassword) {
messageEl.textContent = '请填写所有字段';
messageEl.style.color = 'red';
return;
}
if (newPassword !== confirmPassword) {
messageEl.textContent = '两次输入的新密码不一致';
messageEl.style.color = 'red';
return;
}
try {
// 重新获取最新用户会话
const latestUser = window.electronAPI.getSession('userSession');
console.log('修改密码,用户信息:', latestUser);
if (!latestUser || !latestUser.sessionId) {
messageEl.textContent = '用户会话已过期,请重新登录';
messageEl.style.color = 'red';
setTimeout(() => {
closePasswordModal();
window.electronAPI.navigateTo('login');
}, 2000);
return;
}
const result = await window.api.request('/change-password', 'POST', {
sessionId: latestUser.sessionId,
oldPassword: oldPassword,
newPassword: newPassword,
confirmPassword: confirmPassword
});
console.log('修改密码响应:', result);
if (result.success) {
messageEl.textContent = '密码修改成功';
messageEl.style.color = 'green';
// 更新本地会话中的密码信息(如果需要)
// 注意:这里只更新本地显示,实际密码已在后端更新
setTimeout(() => {
closePasswordModal();
}, 2000);
} else {
messageEl.textContent = result.message;
messageEl.style.color = 'red';
// 如果是会话问题,清除会话
if (result.message.includes('会话') || result.message.includes('登录')) {
window.electronAPI.removeSession('userSession');
setTimeout(() => {
closePasswordModal();
window.electronAPI.navigateTo('login');
}, 2000);
}
}
} catch (error) {
console.error('修改密码错误:', error);
messageEl.textContent = '修改密码时发生错误: ' + error.message;
messageEl.style.color = 'red';
}
}
</script>
</body>
</html>

@ -0,0 +1,264 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数学学习系统 - 答题</title>
<link rel="stylesheet" href="../style/common.css">
<style>
.quiz-container {
max-width: 800px;
margin: 20px auto;
padding: 30px;
background: white;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.progress {
margin-bottom: 30px;
}
.question {
font-size: 20px;
margin-bottom: 30px;
line-height: 1.6;
}
.options {
margin-bottom: 30px;
}
.option {
display: block;
padding: 15px;
margin-bottom: 10px;
border: 2px solid #ddd;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
.option:hover {
background: #f8f9fa;
}
.option.selected {
border-color: #007acc;
background: #e7f3ff;
}
.navigation {
display: flex;
justify-content: space-between;
}
button {
padding: 12px 24px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
.prev {
background: #6c757d;
color: white;
}
.next {
background: #007acc;
color: white;
}
.submit {
background: #28a745;
color: white;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
</style>
</head>
<body>
<div class="quiz-container">
<div class="progress" id="progress">
<!-- 进度信息将通过JavaScript填充 -->
</div>
<div class="question" id="question">
<!-- 题目内容将通过JavaScript填充 -->
</div>
<div class="options" id="options">
<!-- 选项将通过JavaScript填充 -->
</div>
<div class="navigation">
<button class="prev" id="prevBtn">上一题</button>
<button class="next" id="nextBtn">下一题</button>
<button class="submit" id="submitBtn" style="display: none;">提交答案</button>
</div>
</div>
<script>
let currentUser = null;
let currentExam = null;
let currentQuestionIndex = 0;
document.addEventListener('DOMContentLoaded', async () => {
currentUser = window.electronAPI.getSession('userSession');
currentExam = window.electronAPI.getSession('currentExam');
if (!currentUser || !currentExam) {
await window.electronAPI.navigateTo('main');
return;
}
currentQuestionIndex = currentExam.currentQuestion || 0;
await loadQuestion(currentQuestionIndex);
// 绑定事件
document.getElementById('prevBtn').addEventListener('click', () => {
if (currentQuestionIndex > 0) {
currentQuestionIndex--;
loadQuestion(currentQuestionIndex);
}
});
document.getElementById('nextBtn').addEventListener('click', () => {
if (currentQuestionIndex < currentExam.totalQuestions - 1) {
currentQuestionIndex++;
loadQuestion(currentQuestionIndex);
}
});
document.getElementById('submitBtn').addEventListener('click', async () => {
const result = await window.api.request('/finish-exam', 'POST', {
sessionId: currentUser.sessionId
});
if (result.success) {
window.electronAPI.setSession('examResult', result.data);
window.electronAPI.removeSession('currentExam');
await window.electronAPI.navigateTo('result');
} else {
alert('提交失败: ' + result.message);
}
});
});
async function loadQuestion(index) {
try {
console.log(`加载第 ${index + 1} 题,用户会话:`, currentUser);
const result = await window.api.request('/get-question', 'POST', {
sessionId: currentUser.sessionId,
questionIndex: index
});
console.log('获取题目响应:', result);
if (result.success) {
// 更新进度
document.getElementById('progress').innerHTML = `
<strong>第 ${index + 1} 题 / 共 ${result.data.totalQuestions} 题</strong>
<br><small>考试ID: ${result.data.examId}</small>
`;
// 显示题目
const questionEl = document.getElementById('question');
questionEl.innerHTML = `<strong>题目 ${index + 1}:</strong> ${result.data.question}`;
// 显示选项
const optionsContainer = document.getElementById('options');
optionsContainer.innerHTML = '';
if (!result.data.options || result.data.options.length === 0) {
optionsContainer.innerHTML = '<div class="option">暂无选项</div>';
return;
}
result.data.options.forEach((option, i) => {
const optionEl = document.createElement('div');
optionEl.className = 'option';
optionEl.innerHTML = `
<strong>${String.fromCharCode(65 + i)}.</strong> ${option}
`;
// 检查是否是当前选中的答案
if (result.data.userAnswer === i) {
optionEl.classList.add('selected');
}
optionEl.addEventListener('click', async () => {
console.log(`用户选择选项 ${i}: ${option}`);
// 移除其他选项的选中状态
document.querySelectorAll('.option').forEach(opt => {
opt.classList.remove('selected');
});
// 选中当前选项
optionEl.classList.add('selected');
try {
// 提交答案
const submitResult = await window.api.request('/submit-answer', 'POST', {
sessionId: currentUser.sessionId,
questionIndex: index,
answerIndex: i
});
console.log('提交答案响应:', submitResult);
if (!submitResult.success) {
console.error('提交答案失败:', submitResult.message);
// 回退选中状态
optionEl.classList.remove('selected');
}
} catch (error) {
console.error('提交答案错误:', error);
// 回退选中状态
optionEl.classList.remove('selected');
}
});
optionsContainer.appendChild(optionEl);
});
// 更新导航按钮状态
document.getElementById('prevBtn').disabled = index === 0;
document.getElementById('nextBtn').style.display =
index < result.data.totalQuestions - 1 ? 'inline-block' : 'none';
document.getElementById('submitBtn').style.display =
index === result.data.totalQuestions - 1 ? 'inline-block' : 'none';
// 保存当前题目索引
currentExam.currentQuestion = index;
window.electronAPI.setSession('currentExam', currentExam);
console.log(`第 ${index + 1} 题加载完成`);
} else {
console.error('加载题目失败:', result.message);
alert('加载题目失败: ' + result.message);
// 如果是会话问题,跳转到登录页
if (result.message.includes('会话') || result.message.includes('登录')) {
window.electronAPI.removeSession('userSession');
window.electronAPI.removeSession('currentExam');
await window.electronAPI.navigateTo('login');
}
}
} catch (error) {
console.error('加载题目错误:', error);
alert('加载题目时发生错误: ' + error.message);
}
}
</script>
</body>
</html>

@ -0,0 +1,260 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数学学习系统 - 注册</title>
<link rel="stylesheet" href="../style/common.css">
<style>
.register-container {
max-width: 500px;
margin: 50px auto;
padding: 40px;
background: white;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 20px;
}
input,
select {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
}
button {
width: 100%;
padding: 12px;
background: #28a745;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
margin-bottom: 10px;
}
.secondary {
background: #6c757d;
}
.secondary:hover {
background: #545b62;
}
button:hover {
background: #218838;
}
.code-section {
display: flex;
gap: 10px;
}
.code-section input {
flex: 1;
}
.code-section button {
width: 150px;
}
.countdown {
color: #666;
font-size: 14px;
margin-top: 5px;
}
.timer {
color: #dc3545;
font-weight: bold;
}
</style>
</head>
<body>
<div class="register-container">
<h2>用户注册</h2>
<form id="registerForm">
<div class="form-group">
<input type="email" id="email" placeholder="请输入真实邮箱地址" required>
</div>
<div class="form-group">
<div class="code-section">
<input type="text" id="registrationCode" placeholder="请输入邮箱收到的验证码" required>
<button type="button" id="getCodeBtn" class="secondary">获取验证码</button>
</div>
<div id="countdown" class="countdown" style="display: none;">
验证码已发送,<span id="timer" class="timer">60</span>秒后可以重新获取
</div>
</div>
<div class="form-group">
<input type="text" id="username" placeholder="设置用户名" required>
</div>
<div class="form-group">
<input type="password" id="password" placeholder="设置密码 (6-10位包含大小写字母和数字)" required>
</div>
<div class="form-group">
<input type="password" id="confirmPassword" placeholder="确认密码" required>
</div>
<div class="form-group">
<select id="userType" required>
<option value="">选择用户类型</option>
<option value="小学">小学</option>
<option value="初中">初中</option>
<option value="高中">高中</option>
</select>
</div>
<button type="submit">完成注册</button>
</form>
<button class="secondary" id="backToLogin">返回登录</button>
<div id="message" class="message"></div>
</div>
<script>
let countdownTimer = null;
let countdownTime = 60;
// 修改前端API请求确保路径正确
document.getElementById('getCodeBtn').addEventListener('click', async () => {
const email = document.getElementById('email').value;
if (!email) {
showMessage('请输入邮箱地址', 'error');
return;
}
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showMessage('请输入有效的邮箱地址', 'error');
return;
}
try {
// 禁用获取验证码按钮
const getCodeBtn = document.getElementById('getCodeBtn');
getCodeBtn.disabled = true;
getCodeBtn.textContent = '发送中...';
// 调用后端API获取验证码 - 使用正确的路径
const result = await window.api.request('/api/send-registration-code', 'POST', {
email: email
});
if (result.success) {
showMessage('验证码已发送到您的邮箱,请查收', 'success');
startCountdown();
} else {
showMessage(result.message || '发送验证码失败', 'error');
getCodeBtn.disabled = false;
getCodeBtn.textContent = '获取验证码';
}
} catch (error) {
console.error('获取验证码错误:', error);
showMessage('网络错误,请检查服务器连接', 'error');
const getCodeBtn = document.getElementById('getCodeBtn');
getCodeBtn.disabled = false;
getCodeBtn.textContent = '获取验证码';
}
});
document.getElementById('registerForm').addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('email').value;
const registrationCode = document.getElementById('registrationCode').value;
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirmPassword').value;
const userType = document.getElementById('userType').value;
// 表单验证
if (!email || !registrationCode || !username || !password || !confirmPassword || !userType) {
showMessage('请填写所有必填字段', 'error');
return;
}
if (password !== confirmPassword) {
showMessage('两次输入的密码不一致', 'error');
return;
}
// 密码强度验证
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,10}$/;
if (!passwordRegex.test(password)) {
showMessage('密码必须为6-10位且包含大小写字母和数字', 'error');
return;
}
try {
const result = await window.api.request('/api/register', 'POST', {
email, registrationCode, username, password, confirmPassword, userType
});
if (result.success) {
showMessage('注册成功3秒后自动跳转到登录页面', 'success');
setTimeout(() => {
window.electronAPI.navigateTo('login');
}, 3000);
} else {
showMessage(result.message, 'error');
}
} catch (error) {
console.error('注册错误:', error);
showMessage('注册失败,请检查网络连接', 'error');
}
});
document.getElementById('backToLogin').addEventListener('click', async () => {
await window.electronAPI.navigateTo('login');
});
function startCountdown() {
const countdownEl = document.getElementById('countdown');
const timerEl = document.getElementById('timer');
const getCodeBtn = document.getElementById('getCodeBtn');
countdownEl.style.display = 'block';
getCodeBtn.disabled = true;
countdownTimer = setInterval(() => {
countdownTime--;
timerEl.textContent = countdownTime;
if (countdownTime <= 0) {
clearInterval(countdownTimer);
countdownEl.style.display = 'none';
getCodeBtn.disabled = false;
getCodeBtn.textContent = '重新获取验证码';
countdownTime = 60;
}
}, 1000);
}
function showMessage(message, type) {
const messageEl = document.getElementById('message');
messageEl.textContent = message;
messageEl.className = `message ${type}`;
messageEl.style.display = 'block';
// 3秒后自动隐藏成功消息
if (type === 'success') {
setTimeout(() => {
messageEl.style.display = 'none';
}, 3000);
}
}
</script>
</body>
</html>

@ -0,0 +1,104 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数学学习系统 - 成绩</title>
<link rel="stylesheet" href="../style/common.css">
<style>
.result-container {
max-width: 600px;
margin: 50px auto;
padding: 40px;
background: white;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
text-align: center;
}
.score {
font-size: 48px;
font-weight: bold;
color: #28a745;
margin: 20px 0;
}
.btn-group {
display: flex;
gap: 15px;
margin-top: 30px;
}
button {
flex: 1;
padding: 15px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
.primary {
background: #007acc;
color: white;
}
.success {
background: #28a745;
color: white;
}
</style>
</head>
<body>
<div class="result-container">
<h2>考试完成</h2>
<div class="score" id="score">
<!-- 分数将通过JavaScript填充 -->
</div>
<p id="resultText"><!-- 结果文本将通过JavaScript填充 --></p>
<div class="btn-group">
<button class="primary" id="continueBtn">继续做题</button>
<button class="success" id="mainBtn">返回主页</button>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const result = window.electronAPI.getSession('examResult');
if (!result) {
window.electronAPI.navigateTo('main');
return;
}
const score = result.score.toFixed(1);
document.getElementById('score').textContent = `${score}分`;
let resultText = '';
if (score >= 90) {
resultText = '优秀!你做得非常棒!';
} else if (score >= 70) {
resultText = '良好!继续努力!';
} else if (score >= 60) {
resultText = '及格!还有提升空间!';
} else {
resultText = '需要加油哦!多多练习!';
}
document.getElementById('resultText').textContent =
`你在 ${result.totalQuestions} 道题中答对了 ${Math.round(result.totalQuestions * score / 100)} 道题。${resultText}`;
document.getElementById('continueBtn').addEventListener('click', async () => {
await window.electronAPI.navigateTo('main');
});
document.getElementById('mainBtn').addEventListener('click', async () => {
await window.electronAPI.navigateTo('main');
});
});
</script>
</body>
</html>

@ -0,0 +1,37 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.message {
padding: 10px;
margin-top: 15px;
border-radius: 5px;
text-align: center;
}
.message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.message.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}

@ -0,0 +1,6 @@
@echo off
echo 启动数学学习系统后端服务器...
cd java-backend
javac math_question.java
java math_question
pause
Loading…
Cancel
Save