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,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,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,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…
Reference in new issue