Compare commits
4 Commits
d278752fa8
...
ac094b98a4
Author | SHA1 | Date |
---|---|---|
luoyonghuang | ac094b98a4 | 1 month ago |
luoyonghuang | e943e599f5 | 1 month ago |
luoyonghuang | 82d8a93396 | 2 months ago |
luoyonghuang | cbdf44c4d9 | 2 months ago |
@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
@ -0,0 +1,24 @@
|
|||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
|
||||||
|
// 创建连接池
|
||||||
|
const pool = mysql.createPool({
|
||||||
|
host: 'localhost', // 数据库地址
|
||||||
|
user: 'root', // 数据库用户名
|
||||||
|
password: '123456', // 数据库密码
|
||||||
|
database: 'class_k' ,// 使用的数据库
|
||||||
|
waitForConnections: true,
|
||||||
|
connectionLimit: 10,
|
||||||
|
queueLimit: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试数据库连接
|
||||||
|
pool.getConnection((err, connection) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error connecting to the database:', err);
|
||||||
|
} else {
|
||||||
|
console.log('Database connected successfully!');
|
||||||
|
connection.release(); // 释放连接
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = pool;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^1.20.3",
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
|
"express": "^4.21.0",
|
||||||
|
"multer": "^1.4.5-lts.1",
|
||||||
|
"mysql2": "^3.11.3",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>回答问题</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #283c86, #45a247); /* 深蓝到绿色的渐变 */
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(50, 50, 70, 0.9); /* 更深的背景色 */
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
backdrop-filter: blur(10px); /* 增加模糊效果 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#question {
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); /* 文字阴影 */
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: linear-gradient(45deg, #fc5c7d, #6a82fb); /* 渐变按钮 */
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, background 0.3s, box-shadow 0.3s;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: linear-gradient(45deg, #e54d84, #5a72d6); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="question">请重复问题,选择正确或错误:</div>
|
||||||
|
<button id="repeatCorrectButton">准确重复</button>
|
||||||
|
<button id="repeatWrongButton">未准确重复</button>
|
||||||
|
|
||||||
|
<div id="questionResult"></div> <!-- 显示重复问题的结果 -->
|
||||||
|
|
||||||
|
<div id="answerContainer" style="display:none;">
|
||||||
|
<p>现在回答问题:</p>
|
||||||
|
<button id="correctButton">回答正确</button>
|
||||||
|
<button id="partiallyCorrectButton">部分正确</button>
|
||||||
|
<button id="wrongButton">回答错误</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="back-button" onclick="window.location.href='select.html'">返回点名页面</button>
|
||||||
|
<div id="result"></div>
|
||||||
|
<div id="studentInfo"></div> <!-- 显示学生信息 -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function updateScore(student_id, points) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/update-score', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ student_id, points }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('网络响应不正常');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.text(); // 使用 text() 解析纯数字响应
|
||||||
|
console.log('更新后的分数:', data); // data 是字符串形式的分数
|
||||||
|
return data; // 返回更新后的分数字符串
|
||||||
|
} catch (error) {
|
||||||
|
console.error('分数更新失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const student_name = urlParams.get('student_name');
|
||||||
|
const student_id = urlParams.get('student_id');
|
||||||
|
|
||||||
|
const repeatCorrectButton = document.querySelector('#repeatCorrectButton');
|
||||||
|
const repeatWrongButton = document.querySelector('#repeatWrongButton');
|
||||||
|
const questionResultDisplay = document.querySelector('#questionResult');
|
||||||
|
const correctButton = document.querySelector('#correctButton');
|
||||||
|
const partiallyCorrectButton = document.querySelector('#partiallyCorrectButton');
|
||||||
|
const wrongButton = document.querySelector('#wrongButton');
|
||||||
|
const resultDisplay = document.querySelector('#result');
|
||||||
|
const answerContainer = document.querySelector('#answerContainer');
|
||||||
|
const studentInfoDisplay = document.querySelector('#studentInfo');
|
||||||
|
|
||||||
|
// 显示学生姓名和学号
|
||||||
|
studentInfoDisplay.textContent = `学生: ${student_name} 学号: ${student_id}`;
|
||||||
|
|
||||||
|
// 正确重复问题处理 +0.5
|
||||||
|
repeatCorrectButton.addEventListener('click', () => {
|
||||||
|
updateScore(student_id, 0.5)
|
||||||
|
.then(newscore => {
|
||||||
|
if (newscore !== null) {
|
||||||
|
questionResultDisplay.textContent = `重复正确!${student_id} 的当前积分: ${newscore}`; // 直接使用字符串
|
||||||
|
answerContainer.style.display = 'block'; // 显示回答问题部分
|
||||||
|
} else {
|
||||||
|
console.error('更新分数失败');
|
||||||
|
questionResultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
questionResultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 未准确重复问题处理 -1
|
||||||
|
repeatWrongButton.addEventListener('click', () => {
|
||||||
|
updateScore(student_id, -1)
|
||||||
|
.then(newscore => {
|
||||||
|
if (newscore !== null) {
|
||||||
|
questionResultDisplay.textContent = `重复错误!${student_id} 的当前积分: ${newscore}`; // 直接使用字符串
|
||||||
|
answerContainer.style.display = 'block'; // 显示回答问题部分
|
||||||
|
} else {
|
||||||
|
console.error('更新分数失败');
|
||||||
|
questionResultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
questionResultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 回答问题的处理
|
||||||
|
correctButton.addEventListener('click', () => {
|
||||||
|
updateScore(student_id, 3)
|
||||||
|
.then(newscore => {
|
||||||
|
if (newscore !== null) {
|
||||||
|
resultDisplay.textContent = `回答完全正确!${student_id} 的当前积分: ${newscore}`; // 直接使用字符串
|
||||||
|
} else {
|
||||||
|
console.error('更新分数失败');
|
||||||
|
resultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
resultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
partiallyCorrectButton.addEventListener('click', () => {
|
||||||
|
updateScore(student_id, 1.5)
|
||||||
|
.then(newscore => {
|
||||||
|
if (newscore !== null) {
|
||||||
|
resultDisplay.textContent = `回答部分正确!${student_id} 的当前积分: ${newscore}`; // 直接使用字符串
|
||||||
|
} else {
|
||||||
|
console.error('更新分数失败');
|
||||||
|
resultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
resultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
wrongButton.addEventListener('click', () => {
|
||||||
|
updateScore(student_id, 0.5)
|
||||||
|
.then(newscore => {
|
||||||
|
if (newscore !== null) {
|
||||||
|
resultDisplay.textContent = `回答基本正确!${student_id} 的当前积分: ${newscore}`; // 直接使用字符串
|
||||||
|
} else {
|
||||||
|
console.error('更新分数失败');
|
||||||
|
resultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
resultDisplay.textContent = `更新分数失败,请重试。`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>课堂管理系统</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #2C3E50, #4CA1AF); /* 深色渐变背景 */
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #ECF0F1; /* 浅色文字 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(52, 73, 94, 0.9); /* 更深的透明背景 */
|
||||||
|
padding: 50px;
|
||||||
|
border-radius: 25px; /* 更加圆润的边角 */
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); /* 增强阴影效果 */
|
||||||
|
backdrop-filter: blur(15px); /* 背景模糊效果 */
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
font-size: 2.5em;
|
||||||
|
letter-spacing: 1px; /* 字母间距 */
|
||||||
|
text-transform: uppercase; /* 大写 */
|
||||||
|
text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.3); /* 字体阴影 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 15px 25px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: linear-gradient(45deg, #FF6F61, #FF3D00); /* 动感渐变按钮 */
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, background 0.3s, box-shadow 0.3s;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
outline: none; /* 去掉按钮轮廓 */
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: linear-gradient(45deg, #FF4D00, #FF6F61); /* 悬停时的渐变变化 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.button-group {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>课堂管理系统</h1>
|
||||||
|
<div class="button-group">
|
||||||
|
<button onclick="alert('开始点名')">开始点名</button>
|
||||||
|
<button onclick="alert('查看成绩')">查看成绩</button>
|
||||||
|
<button onclick="alert('管理学生')">管理学生</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,79 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>课堂管理系统</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #283c86, #45a247); /* 深蓝到绿色的渐变 */
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(50, 50, 70, 0.9); /* 更深的背景色 */
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
backdrop-filter: blur(10px); /* 增加模糊效果 */
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
font-size: 2.5em;
|
||||||
|
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); /* 文字阴影 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: linear-gradient(45deg, #6a82fb, #fc5c7d); /* 渐变按钮 */
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, background 0.3s, box-shadow 0.3s;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: linear-gradient(45deg, #5a72d6, #e54d84); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.button-group {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>课堂管理系统</h1>
|
||||||
|
<div class="button-group">
|
||||||
|
<button onclick="alert('开始点名')">开始点名</button>
|
||||||
|
<button onclick="alert('查看成绩')">查看成绩</button>
|
||||||
|
<button onclick="alert('管理学生')">管理学生</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,209 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>抽奖页面</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #283c86, #45a247); /* 深蓝到绿色的渐变 */
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #ffffff;
|
||||||
|
transition: background-color 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(50, 50, 70, 0.9); /* 更深的背景色 */
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
backdrop-filter: blur(10px); /* 增加模糊效果 */
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pointsDisplay {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); /* 文字阴影 */
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: linear-gradient(45deg, #fc5c7d, #6a82fb); /* 渐变按钮 */
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, background 0.3s, box-shadow 0.3s;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: linear-gradient(45deg, #e54d84, #5a72d6); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading {
|
||||||
|
display: none;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="pointsDisplay">当前积分: </div>
|
||||||
|
<button id="startLotteryButton">开始抽奖</button>
|
||||||
|
<button class="back-button" id="backButton">返回前一页面</button>
|
||||||
|
<div id="result"></div>
|
||||||
|
<div id="loading">正在加载,请稍候...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
let student_name = urlParams.get('student_name');
|
||||||
|
let student_id = urlParams.get('student_id');
|
||||||
|
|
||||||
|
const pointsDisplay = document.querySelector('#pointsDisplay');
|
||||||
|
const startLotteryButton = document.querySelector('#startLotteryButton');
|
||||||
|
const resultDisplay = document.querySelector('#result');
|
||||||
|
const backButton = document.getElementById('backButton');
|
||||||
|
const loadingIndicator = document.getElementById('loading');
|
||||||
|
|
||||||
|
// 睡眠函数,让页面跳转更自然
|
||||||
|
function sleep(d) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局变量,用于存储用户积分
|
||||||
|
let userPoints = null;
|
||||||
|
|
||||||
|
// 获取该用户的分数
|
||||||
|
const fetchStudentScore = async (student_id) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:3000/api/get-score', {
|
||||||
|
method: 'POST', // 使用 POST 方法
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json', // 设置请求体的内容类型
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ student_id }), // 将 student_id 放入请求体
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
alert('分数获取失败');
|
||||||
|
return null; // 返回 null 表示获取失败
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接返回响应体中的数字
|
||||||
|
const data = await response.json(); // 直接解析为 JSON
|
||||||
|
const score = data.score;
|
||||||
|
return score; // 返回数字
|
||||||
|
} catch (error) {
|
||||||
|
alert('网络请求失败');
|
||||||
|
return null; // 返回 null 表示获取失败
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 更新用户分数
|
||||||
|
async function updateScore(student_id, points) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:3000/api/update-score', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ student_id, points }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('网络响应不正常');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 text() 解析响应,得到字符串形式的分数
|
||||||
|
const data = await response.text();
|
||||||
|
let score = Number(data); // 转换为数字
|
||||||
|
return score; // 返回数字
|
||||||
|
} catch (error) {
|
||||||
|
console.error('分数更新失败:', error);
|
||||||
|
return null; // 返回 null 表示更新失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 初始化函数,用于设置用户积分
|
||||||
|
async function init() {
|
||||||
|
userPoints = await fetchStudentScore(student_id); // 获取分数并存储到全局变量
|
||||||
|
pointsDisplay.textContent = `${student_name} 的当前积分: ${userPoints}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行初始化函数
|
||||||
|
init();
|
||||||
|
|
||||||
|
// 设置返回按钮的 URL
|
||||||
|
backButton.onclick = () => {
|
||||||
|
loadingIndicator.style.display = 'block'; // 显示加载提示
|
||||||
|
window.location.href = `choice.html?student_name=${encodeURIComponent(student_name)}&student_id=${encodeURIComponent(student_id)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始抽奖
|
||||||
|
startLotteryButton.addEventListener('click', async () => {
|
||||||
|
if (userPoints >= 2) {
|
||||||
|
userPoints = await updateScore(student_id, -2); // 积分-2
|
||||||
|
|
||||||
|
pointsDisplay.textContent = `${student_name} 的当前积分: ${userPoints}`;
|
||||||
|
|
||||||
|
// 随机选择奖品
|
||||||
|
const prizes = ["跳过权", "再来一次", "什么也没有发生", "加3积分且跳过"];
|
||||||
|
const randomPrize = prizes[Math.floor(Math.random() * prizes.length)];
|
||||||
|
resultDisplay.textContent = `你赢得了: ${randomPrize}`;
|
||||||
|
|
||||||
|
// 等待 2 秒以展示结果
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
loadingIndicator.style.display = 'block'; // 显示加载提示
|
||||||
|
|
||||||
|
if (randomPrize === '跳过权') {
|
||||||
|
await sleep(2000);
|
||||||
|
window.location.href = `select.html?student_name=${encodeURIComponent(student_name)}&student_id=${encodeURIComponent(student_id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (randomPrize === '再来一次') {
|
||||||
|
userPoints = await updateScore(student_id, 2); // 积分返回2
|
||||||
|
await sleep(2000);
|
||||||
|
window.location.href = `lottery.html?student_name=${encodeURIComponent(student_name)}&student_id=${encodeURIComponent(student_id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (randomPrize === '什么也没有发生') {
|
||||||
|
await sleep(2000);
|
||||||
|
window.location.href = `answer.html?student_name=${encodeURIComponent(student_name)}&student_id=${encodeURIComponent(student_id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (randomPrize === '加3积分且跳过') {
|
||||||
|
userPoints = await updateScore(student_id, 3); // 积分+3
|
||||||
|
await sleep(2000);
|
||||||
|
window.location.href = `select.html?student_name=${encodeURIComponent(student_name)}&student_id=${encodeURIComponent(student_id)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resultDisplay.textContent = "积分不足,无法抽奖!返回回答问题";
|
||||||
|
loadingIndicator.style.display = 'block'; // 显示加载提示
|
||||||
|
await sleep(2000);
|
||||||
|
window.location.href = `answer.html?student_name=${encodeURIComponent(student_name)}&student_id=${encodeURIComponent(student_id)}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,242 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>积分显示</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #283c86, #45a247); /* 深蓝到绿色的渐变 */
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #ffffff; /* 白色字体 */
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 800px;
|
||||||
|
background: rgba(50, 50, 70, 0.9); /* 更深的背景色 */
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
backdrop-filter: blur(10px); /* 增加模糊效果 */
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
font-size: 2em;
|
||||||
|
color: #ff5722; /* 标题颜色 */
|
||||||
|
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); /* 文字阴影 */
|
||||||
|
}
|
||||||
|
#sortButton {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: linear-gradient(45deg, #6a82fb, #fc5c7d); /* 渐变按钮 */
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, background 0.3s, box-shadow 0.3s;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
#sortButton:hover {
|
||||||
|
background: linear-gradient(45deg, #5a72d6, #e54d84); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.2); /* 边框颜色 */
|
||||||
|
font-size: 1em;
|
||||||
|
color: #ffffff; /* 字体颜色 */
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #4CAF50; /* 表头背景颜色 */
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
tr:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1); /* 悬停行的颜色 */
|
||||||
|
}
|
||||||
|
.button-group {
|
||||||
|
margin-top: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.back-button, .clear-button {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.2s, box-shadow 0.3s;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
.back-button {
|
||||||
|
background: linear-gradient(45deg, #6a82fb, #fc5c7d); /* 渐变背景 */
|
||||||
|
}
|
||||||
|
.back-button:hover {
|
||||||
|
background: linear-gradient(45deg, #5a72d6, #e54d84); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
.clear-button {
|
||||||
|
background: linear-gradient(45deg, #f44336, #e53935); /* 渐变背景 */
|
||||||
|
}
|
||||||
|
.clear-button:hover {
|
||||||
|
background: linear-gradient(45deg, #d32f2f, #c62828); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
th, td {
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
#sortButton, .button-group button {
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>2024K班积分排名</h1>
|
||||||
|
<button id="sortButton">切换排序 (降序)</button>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>姓名</th>
|
||||||
|
<th>学号</th>
|
||||||
|
<th>积分</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="studentList">
|
||||||
|
<!-- 学生积分将在此处显示 -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="button-group">
|
||||||
|
<button class="back-button" onclick="window.location.href='index.html'">返回主页面</button>
|
||||||
|
<button class="clear-button" id="clearButton">清除所有数据</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const studentList = document.getElementById('studentList');
|
||||||
|
const sortButton = document.getElementById('sortButton');
|
||||||
|
const clearButton = document.getElementById('clearButton');
|
||||||
|
let ascend = false; // 默认降序
|
||||||
|
|
||||||
|
|
||||||
|
//删除表
|
||||||
|
async function deleteTable(tableName) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/delete-table', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ tableName }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('删除表失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
alert(data.message); // 成功后显示消息
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除表操作失败:', error);
|
||||||
|
alert('删除表失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 获取按分数降序排列的学生排名
|
||||||
|
async function fetchDescendRanking() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/descend_ranking');
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ranking = await response.json(); // 获取并解析为JSON数组
|
||||||
|
return ranking; // 返回排名数组
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取按分数升序排列的学生排名
|
||||||
|
async function fetchAscendRanking() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/ascend_ranking');
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ranking = await response.json(); // 获取并解析为JSON数组
|
||||||
|
return ranking; // 返回排名数组
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function displayStudents() {
|
||||||
|
let ranking; // 声明 ranking 变量
|
||||||
|
if (ascend) {
|
||||||
|
ranking = await fetchAscendRanking(); // 获取升序排名
|
||||||
|
} else {
|
||||||
|
ranking = await fetchDescendRanking(); // 获取降序排名
|
||||||
|
}
|
||||||
|
|
||||||
|
studentList.innerHTML = ''; // 清空表格内容
|
||||||
|
// 只显示前10名学生
|
||||||
|
ranking.slice(0, 10).forEach(student => {
|
||||||
|
studentList.innerHTML += `
|
||||||
|
<tr>
|
||||||
|
<td>${student.student_name}</td> <!-- 学生姓名 -->
|
||||||
|
<td>${student.student_id}</td> <!-- 学生ID -->
|
||||||
|
<td>${student.score}</td> <!-- 学生分数 -->
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sortButton.addEventListener('click', () => {
|
||||||
|
ascend = !ascend;
|
||||||
|
sortButton.textContent = `切换排序 (${ascend ? '升序' : '降序'})`;
|
||||||
|
displayStudents();
|
||||||
|
});
|
||||||
|
|
||||||
|
clearButton.addEventListener('click', () => {
|
||||||
|
const confirmation = confirm("确定要清除所有学生数据吗?此操作无法撤销!");
|
||||||
|
if (confirmation) {
|
||||||
|
deleteTable('students');
|
||||||
|
displayStudents(); // 重新显示清空后的数据
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
displayStudents();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,165 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>随机点名</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #283c86, #45a247); /* 深蓝到绿色的渐变 */
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(50, 50, 70, 0.9); /* 更深的背景色 */
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
backdrop-filter: blur(10px); /* 增加模糊效果 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#nameDisplay {
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); /* 文字阴影 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#nameDisplay.active {
|
||||||
|
transform: scale(1.1);
|
||||||
|
color: #ffeb3b;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: linear-gradient(45deg, #fc5c7d, #6a82fb); /* 渐变按钮 */
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, background 0.3s, box-shadow 0.3s;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: linear-gradient(45deg, #e54d84, #5a72d6); /* 悬停时加深 */
|
||||||
|
transform: translateY(-3px); /* 悬停时上移效果 */
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
background-color: #999;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
button + button {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="nameDisplay">准备开始提问</div>
|
||||||
|
<button id="startButton">开始</button>
|
||||||
|
<button id="stopButton" disabled>停止</button>
|
||||||
|
<button class="back-button" onclick="window.location.href='index.html'">返回主页面</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let names = [];
|
||||||
|
let intervalId = null;
|
||||||
|
|
||||||
|
// 获取 DOM 元素
|
||||||
|
const nameDisplay = document.querySelector('#nameDisplay');
|
||||||
|
const startButton = document.querySelector('#startButton');
|
||||||
|
const stopButton = document.querySelector('#stopButton');
|
||||||
|
|
||||||
|
// 页面加载时获取学生名单
|
||||||
|
window.addEventListener('DOMContentLoaded', fetchStudents);
|
||||||
|
|
||||||
|
// 获取学生名单
|
||||||
|
async function fetchStudents() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/get-students');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
names = data.map(student => student.student_name);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching student list:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态更新显示效果
|
||||||
|
const updateNameDisplay = (name) => {
|
||||||
|
nameDisplay.textContent = `点到: ${name}`;
|
||||||
|
nameDisplay.classList.add('active');
|
||||||
|
setTimeout(() => {
|
||||||
|
nameDisplay.classList.remove('active');
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始滚动显示
|
||||||
|
startButton.addEventListener('click', () => {
|
||||||
|
startButton.disabled = true;
|
||||||
|
stopButton.disabled = false;
|
||||||
|
|
||||||
|
intervalId = setInterval(() => {
|
||||||
|
// 从 names 数组中随机选择一个名字进行滚动显示
|
||||||
|
const randomIndex = Math.floor(Math.random() * names.length);
|
||||||
|
const selectedName = names[randomIndex];
|
||||||
|
|
||||||
|
// 更新显示选中的名字
|
||||||
|
updateNameDisplay(selectedName);
|
||||||
|
}, 100); // 每100毫秒滚动显示一次
|
||||||
|
});
|
||||||
|
|
||||||
|
// 停止滚动并通过后端决定最终选择的学生
|
||||||
|
stopButton.addEventListener('click', async () => {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
startButton.disabled = false;
|
||||||
|
stopButton.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/random-call');
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
// 从后端返回的数据中获取学生名字
|
||||||
|
const selectedName = data.student_name; // student_name 属性
|
||||||
|
|
||||||
|
if (selectedName) {
|
||||||
|
// 更新显示选中的学生
|
||||||
|
updateNameDisplay(selectedName);
|
||||||
|
// 跳转到新页面并传递被选中的名字
|
||||||
|
window.location.href = `choice.html?student_name=${encodeURIComponent(data.student_name)}&student_id=${encodeURIComponent(data.student_id)}`;
|
||||||
|
} else {
|
||||||
|
console.error('未返回有效学生数据');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,51 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const multer = require('multer');
|
||||||
|
const randomSelect = require('./services/randomSelect');
|
||||||
|
const ranking = require('./services/ranking');
|
||||||
|
const classManager = require('./services/classManager');
|
||||||
|
const scoreManager = require('./services/scoreManager');
|
||||||
|
const TableManager = require('./services/delete-table');
|
||||||
|
const pool = require('./db');
|
||||||
|
// 文件上传配置
|
||||||
|
const upload = multer({ dest: 'uploads/' });
|
||||||
|
|
||||||
|
// 路由 - 添加班级
|
||||||
|
router.post('/upload', upload.single('file'), classManager.addClass);
|
||||||
|
|
||||||
|
// 路由 - 随机点名
|
||||||
|
router.get('/random-call', randomSelect.selectStudent);
|
||||||
|
|
||||||
|
// 路由 - 获取排名
|
||||||
|
router.get('/ascend_ranking', ranking.get_ascend_Ranking);
|
||||||
|
router.get('/descend_ranking', ranking.get_descend_Ranking);
|
||||||
|
|
||||||
|
// 路由 - 更新分数
|
||||||
|
router.post('/update-score', scoreManager.updateScore);
|
||||||
|
|
||||||
|
//路由 - 获取某个id的分数
|
||||||
|
router.post('/get-score', scoreManager.getStudentScore);
|
||||||
|
|
||||||
|
//路由 - 删除班级
|
||||||
|
router.post('/delete-table', TableManager.deleteTable);
|
||||||
|
|
||||||
|
//路由 - 导出成绩
|
||||||
|
router.get('/export-scores', scoreManager.exportScores);
|
||||||
|
// 获取全部学生名单
|
||||||
|
router.get('/get-students', async (req, res) => {
|
||||||
|
let connection; // 声明连接变量
|
||||||
|
try {
|
||||||
|
connection = await pool.getConnection(); // 从连接池获取连接
|
||||||
|
const [students] = await connection.query('SELECT student_name FROM students'); // 执行查询
|
||||||
|
const studentNames = students.map(student => ({ student_name: student.student_name })); // 只保留 student_name 字段
|
||||||
|
res.json(studentNames); // 返回只包含 student_name 的结果
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching students:', error); // 记录错误信息
|
||||||
|
res.status(500).send({ error: 'Failed to fetch students' }); // 返回500错误响应
|
||||||
|
} finally {
|
||||||
|
if (connection) connection.release(); // 确保连接被释放
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -0,0 +1,26 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const path = require('path');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const router = require('./router');
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// 中间件
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// API 路由
|
||||||
|
app.use('/api', router);
|
||||||
|
|
||||||
|
// 静态文件
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
|
// 处理 SPA 路由
|
||||||
|
app.get('*', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server running on port ${PORT}`);
|
||||||
|
});
|
@ -0,0 +1,65 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const xlsx = require('xlsx');
|
||||||
|
const pool = require('../db');
|
||||||
|
|
||||||
|
const classManager = {
|
||||||
|
addClass: async (req, res) => {
|
||||||
|
// 创建students表的SQL语句
|
||||||
|
const createTableQuery = `
|
||||||
|
CREATE TABLE IF NOT EXISTS students (
|
||||||
|
student_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
student_name VARCHAR(255) NOT NULL,
|
||||||
|
score DECIMAL(5, 2),
|
||||||
|
call_count INT DEFAULT 0
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
try {
|
||||||
|
// 先创建表
|
||||||
|
await pool.query(createTableQuery);
|
||||||
|
|
||||||
|
const filePath = req.file.path;
|
||||||
|
|
||||||
|
// 读取并解析Excel文件
|
||||||
|
const workbook = xlsx.readFile(filePath);
|
||||||
|
const sheetName = workbook.SheetNames[0];
|
||||||
|
const worksheet = workbook.Sheets[sheetName];
|
||||||
|
|
||||||
|
const students = xlsx.utils.sheet_to_json(worksheet);
|
||||||
|
|
||||||
|
// 定义所需的字段
|
||||||
|
const requiredFields = ['student_id','student_name'];
|
||||||
|
let hasError = false;
|
||||||
|
|
||||||
|
students.forEach(student => {
|
||||||
|
// 数据验证
|
||||||
|
const missingFields = requiredFields.filter(field =>!(field in student));
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
console.error(`Student data is missing fields: ${missingFields.join(', ')}`);
|
||||||
|
hasError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将学生数据插入数据库
|
||||||
|
const query = 'INSERT INTO students (student_id, student_name, score, call_count) VALUES (?,?, 2, 0)';
|
||||||
|
pool.query(query, [student.student_id, student.student_name], (error) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`Error inserting student: ${student.name}`, error);
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
res.status(500).json({ message: 'Error adding class. Some students were not added successfully.' });
|
||||||
|
} else {
|
||||||
|
res.status(200).json({ message: 'Class added successfully!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error creating students table:', err);
|
||||||
|
res.status(500).json({ message: 'Error creating table during class addition.' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = classManager;
|
@ -0,0 +1,22 @@
|
|||||||
|
const pool = require('../db');
|
||||||
|
|
||||||
|
async function deleteTable(req, res) {
|
||||||
|
const { tableName } = req.body; // 从请求体中获取表名
|
||||||
|
let connection;
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection = await pool.getConnection();
|
||||||
|
await connection.query(`DROP TABLE IF EXISTS ??`, [tableName]); // 删除表
|
||||||
|
connection.release();
|
||||||
|
res.json({ message: `成功删除` });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除表失败:', error);
|
||||||
|
res.status(500).json({ error: '删除表失败' });
|
||||||
|
} finally {
|
||||||
|
if (connection) {
|
||||||
|
connection.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { deleteTable };
|
@ -0,0 +1,49 @@
|
|||||||
|
const pool = require('../db');
|
||||||
|
|
||||||
|
async function selectStudent(req, res) {
|
||||||
|
try {
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
|
||||||
|
// 从数据库查询学生
|
||||||
|
const [students] = await connection.query('SELECT * FROM students');
|
||||||
|
|
||||||
|
// 计算权重的总和(使用 softmax 权重计算)
|
||||||
|
const totalWeight = students.reduce((acc, student) => acc + Math.exp(-student.score), 0);
|
||||||
|
|
||||||
|
// 将每个学生的权重归一化
|
||||||
|
const weightedStudents = students.map(student => ({
|
||||||
|
student,
|
||||||
|
weight: Math.exp(-student.score
|
||||||
|
|
||||||
|
) / totalWeight,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 生成随机数
|
||||||
|
const random = Math.random(); // 介于 0 和 1 之间
|
||||||
|
let sum = 0; // 用于累加权重
|
||||||
|
let selectedStudent = null;
|
||||||
|
|
||||||
|
// 遍历加权后的学生,累加权重,并判断随机数落在哪个学生的区间
|
||||||
|
for (let i = 0; i < weightedStudents.length; i++) {
|
||||||
|
sum += weightedStudents[i].weight; // 累加当前学生的权重
|
||||||
|
if (random <= sum) { // 如果随机数小于或等于当前的累积权重
|
||||||
|
selectedStudent = weightedStudents[i].student; // 选中该学生
|
||||||
|
break; // 找到后立即退出循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedStudent) {
|
||||||
|
res.send({ student_name: selectedStudent.student_name,
|
||||||
|
student_id: selectedStudent.student_id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).send({ message: '无学生数据' });
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.release();
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ error: '随机选择学生失败' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { selectStudent };
|
@ -0,0 +1,25 @@
|
|||||||
|
const pool = require('../db');
|
||||||
|
|
||||||
|
async function get_descend_Ranking(req, res) {
|
||||||
|
try {
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
const [ranking] = await connection.query('SELECT student_name, student_id, score FROM students ORDER BY score DESC');
|
||||||
|
connection.release();
|
||||||
|
res.json(ranking);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ error: '无法获取排名' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_ascend_Ranking(req, res) {
|
||||||
|
try {
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
const [ranking] = await connection.query('SELECT student_name, student_id, score FROM students ORDER BY score ASC');
|
||||||
|
connection.release();
|
||||||
|
res.json(ranking);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ error: '无法获取排名' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { get_ascend_Ranking, get_descend_Ranking };
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue