|
|
let students = JSON.parse(localStorage.getItem('students')) || [];
|
|
|
let selectedStudent = null;
|
|
|
let intervalId;
|
|
|
let lock = false;//自动加分锁
|
|
|
let lock1 = false;//手动加分锁
|
|
|
let onClassAdded = false;
|
|
|
let autoPointsAdded = false;
|
|
|
let manualPointsUsed = false;
|
|
|
let studentDrawn = false;
|
|
|
let starLock = false;
|
|
|
|
|
|
const stars = document.querySelectorAll('.rating label');
|
|
|
|
|
|
stars.forEach(star => {
|
|
|
star.addEventListener('mouseenter', () => {
|
|
|
const number = star.querySelector('.star-number');
|
|
|
number.classList.remove('hidden');
|
|
|
});
|
|
|
|
|
|
star.addEventListener('mouseleave', () => {
|
|
|
const number = star.querySelector('.star-number');
|
|
|
number.classList.add('hidden');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
const particleCanvas = document.getElementById('particleCanvas');
|
|
|
const particleCtx = particleCanvas.getContext('2d');
|
|
|
particleCanvas.width = window.innerWidth;
|
|
|
particleCanvas.height = window.innerHeight;
|
|
|
|
|
|
let particles = [];
|
|
|
|
|
|
function createParticlesFromText(a, b) {
|
|
|
// 清空画布
|
|
|
particleCtx.clearRect(0, 0, particleCanvas.width, particleCanvas.height);
|
|
|
|
|
|
// 设置字体
|
|
|
particleCtx.font = '150px "Long Cang"';
|
|
|
particleCtx.fillStyle = '#003366';
|
|
|
|
|
|
// 绘制第一行文字
|
|
|
const x1 = particleCanvas.width / 2 - particleCtx.measureText(selectedStudent.name).width / 2;
|
|
|
const y1 = particleCanvas.height / 2 - 50; // 向上移动50像素
|
|
|
particleCtx.fillText(selectedStudent.name, x1, y1);
|
|
|
|
|
|
// 绘制第二行文字
|
|
|
const x2 = particleCanvas.width / 2 - particleCtx.measureText(selectedStudent.id).width / 2;
|
|
|
const y2 = particleCanvas.height / 2 + 70; // 向下移动70像素
|
|
|
particleCtx.fillText(selectedStudent.id, x2, y2);
|
|
|
|
|
|
// 获取整个画布的像素数据
|
|
|
const imageData = particleCtx.getImageData(0, 0, particleCanvas.width, particleCanvas.height);
|
|
|
|
|
|
// 清空粒子数组
|
|
|
particles = [];
|
|
|
|
|
|
// 创建粒子
|
|
|
for (let y = 0; y < imageData.height; y += 2) {
|
|
|
for (let x = 0; x < imageData.width; x += 2) {
|
|
|
const index = (y * imageData.width + x) * 4;
|
|
|
const alpha = imageData.data[index + 3];
|
|
|
if (alpha > 128) { // 只选择不透明的像素
|
|
|
let particle = new Particle(Math.random() * particleCanvas.width, Math.random() * particleCanvas.height);
|
|
|
particle.targetX = x;
|
|
|
particle.targetY = y;
|
|
|
particles.push(particle);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 启动粒子动画
|
|
|
animateParticles();
|
|
|
}
|
|
|
|
|
|
function Particle(x, y) {
|
|
|
this.x = x;
|
|
|
this.y = y;
|
|
|
this.size = 1;
|
|
|
this.baseX = this.x;
|
|
|
this.baseY = this.y;
|
|
|
this.density = Math.random() * 30 + 1;
|
|
|
}
|
|
|
|
|
|
Particle.prototype.draw = function () {
|
|
|
particleCtx.fillStyle = 'black';
|
|
|
particleCtx.beginPath();
|
|
|
particleCtx.arc(this.x, this.y, this.size, 0, Math.PI * 2); // 绘制粒子
|
|
|
particleCtx.closePath();
|
|
|
particleCtx.fill();
|
|
|
};
|
|
|
|
|
|
Particle.prototype.update = function () {
|
|
|
let dx = this.targetX - this.x;
|
|
|
let dy = this.targetY - this.y;
|
|
|
let distance = Math.sqrt(dx * dx + dy * dy);
|
|
|
let speed = distance / 20; // 移动速度,离目标越远速度越快
|
|
|
this.x += dx / distance * speed;
|
|
|
this.y += dy / distance * speed;
|
|
|
|
|
|
// 减少抖动,当粒子非常接近目标位置时停止移动
|
|
|
if (distance < 1) {
|
|
|
this.x = this.targetX;
|
|
|
this.y = this.targetY;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
function animateParticles() {
|
|
|
particleCtx.clearRect(0, 0, particleCanvas.width, particleCanvas.height); // 清除上一帧的粒子
|
|
|
|
|
|
particles.forEach(particle => {
|
|
|
particle.update(); // 更新每个粒子的状态
|
|
|
particle.draw(); // 绘制每个粒子
|
|
|
});
|
|
|
|
|
|
requestAnimationFrame(animateParticles); // 循环动画
|
|
|
}
|
|
|
|
|
|
function registerTooltipEvents() {
|
|
|
const buttons = document.querySelectorAll('button[data-tooltip]');
|
|
|
const tooltip = document.getElementById('tooltip');
|
|
|
|
|
|
buttons.forEach(button => {
|
|
|
button.addEventListener('mouseenter', function(event) {
|
|
|
tooltip.textContent = button.getAttribute('data-tooltip');
|
|
|
tooltip.style.display = 'block';
|
|
|
tooltip.style.left = event.pageX + 'px';
|
|
|
tooltip.style.top = (event.pageY + 20) + 'px'; // 鼠标下方略微偏移
|
|
|
});
|
|
|
|
|
|
button.addEventListener('mouseleave', function() {
|
|
|
tooltip.style.display = 'none';
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function showMessage(text) {
|
|
|
const messageDiv = document.getElementById("message");
|
|
|
messageDiv.textContent = text;
|
|
|
messageDiv.style.display = "block"; // 显示消息
|
|
|
|
|
|
setTimeout(() => {
|
|
|
messageDiv.style.display = "none"; // 1秒后隐藏消息
|
|
|
}, 1000);
|
|
|
}
|
|
|
|
|
|
window.onload = function() {
|
|
|
// 注册工具提示事件
|
|
|
registerTooltipEvents();
|
|
|
resetStars();
|
|
|
const storedStudent = localStorage.getItem('selectedStudent');//获取selectedStudent
|
|
|
if (storedStudent) {
|
|
|
selectedStudent = JSON.parse(storedStudent);
|
|
|
document.getElementById('selectedStudent').innerText = `${selectedStudent.name} (学号: ${selectedStudent.id})`;
|
|
|
autoPointsAdded = localStorage.getItem('autoPointsAdded') === 'true';
|
|
|
manualPointsUsed = localStorage.getItem('manualPointsUsed') === 'true';
|
|
|
starLock = localStorage.getItem('starLock') === 'true';
|
|
|
onClassAdded = localStorage.getItem('onClassAdded') === 'true';
|
|
|
}
|
|
|
};
|
|
|
|
|
|
function uploadFile() {
|
|
|
const input = document.getElementById('fileInput');
|
|
|
const file = input.files[0];
|
|
|
if (!file) {
|
|
|
showMessage("请选择一个文件!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = function(e) {
|
|
|
const data = new Uint8Array(e.target.result);
|
|
|
const workbook = XLSX.read(data, { type: 'array' });
|
|
|
const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
|
|
|
|
// 读取所有行(包括空行)
|
|
|
let allStudents = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });
|
|
|
|
|
|
// 如果没有数据,则提示用户
|
|
|
if (allStudents.length < 2) {
|
|
|
showMessage("工作表中没有有效数据!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 去掉表头
|
|
|
allStudents = allStudents.slice(1);
|
|
|
|
|
|
// 过滤有效行,确保每行的有效性
|
|
|
students = allStudents
|
|
|
.filter(row => row[0] !== undefined && row[0] !== null && row[0] !== '') // 确保 ID 不为空
|
|
|
.map(row => ({
|
|
|
id: row[0],
|
|
|
name: row[1], // 假设第二列为名字
|
|
|
points: 0 // 默认分数为 0
|
|
|
}));
|
|
|
|
|
|
// 如果没有有效的学生数据,则提示
|
|
|
if (students.length === 0) {
|
|
|
showMessage("工作表中没有有效学生数据!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
localStorage.setItem('students', JSON.stringify(students));
|
|
|
showMessage("学生名单导入成功!");
|
|
|
toggleButtons();
|
|
|
};
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
}
|
|
|
|
|
|
function updateFileName() {
|
|
|
const input = document.getElementById('fileInput');
|
|
|
const fileName = document.getElementById('fileName');
|
|
|
if (input.files.length > 0) {
|
|
|
fileName.innerText = input.files[0].name; // 显示选择的文件名
|
|
|
} else {
|
|
|
fileName.innerText = '未选择文件'; // 显示未选择文件的提示
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function viewStudentList() {
|
|
|
window.location.href = 'studentList.html';
|
|
|
}
|
|
|
|
|
|
function startDraw() {
|
|
|
if (students.length === 0) {
|
|
|
showMessage("请先导入学生名单!");
|
|
|
uploadModal.style.display = 'block'; // 显示模态框
|
|
|
return;
|
|
|
}
|
|
|
// 隐藏页面元素
|
|
|
document.querySelector('.container').classList.add('hidden');
|
|
|
resetStars();
|
|
|
|
|
|
lock = false;
|
|
|
lock1 = false;
|
|
|
onClassAdded = false;
|
|
|
autoPointsAdded = false;
|
|
|
manualPointsUsed = false;
|
|
|
starLock = false;
|
|
|
|
|
|
|
|
|
const totalPoints = students.reduce((sum, student) => sum + (student.points > 10 ? 1 / student.points : 10 - student.points), 0);
|
|
|
const randomValue = Math.random() * totalPoints;
|
|
|
let cumulativePoints = 0;
|
|
|
|
|
|
for (const student of students) {
|
|
|
cumulativePoints += Math.max(1, 10 - student.points);
|
|
|
if (randomValue < cumulativePoints) {
|
|
|
selectedStudent = student;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
setTimeout(scrollStudents, 350);
|
|
|
}
|
|
|
|
|
|
function onClass() {
|
|
|
if(!onClassAdded && selectedStudent) {
|
|
|
selectedStudent.points += 1;
|
|
|
synPoints();
|
|
|
onClassAdded = true;
|
|
|
updateLocalStorage();
|
|
|
showMessage(`${selectedStudent.name}到场,加分 1。`);
|
|
|
} else if (!selectedStudent) {
|
|
|
showMessage("请先抽取一位学生");
|
|
|
} else {
|
|
|
showMessage("到场已加分");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
document.addEventListener('click', async function(event) {
|
|
|
const drawButton = document.getElementById('drawButton');
|
|
|
const scrollingNamesDiv = document.getElementById('scrollingNames');
|
|
|
const selectedStudentDiv = document.getElementById('selectedStudent');
|
|
|
|
|
|
if(!clickEnabled){
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!drawButton.contains(event.target) &&
|
|
|
!scrollingNamesDiv.contains(event.target) &&
|
|
|
!selectedStudentDiv.contains(event.target)) {
|
|
|
|
|
|
// 清空显示的被点到学生信息
|
|
|
scrollingNamesDiv.innerHTML = '';
|
|
|
|
|
|
particles = [];
|
|
|
particleCtx.clearRect(0, 0, particleCanvas.width, particleCanvas.height);
|
|
|
|
|
|
// 恢复页面元素
|
|
|
document.querySelector('.container').classList.remove('hidden');
|
|
|
selectedStudentDiv.style.display = 'block'; // 显示
|
|
|
}
|
|
|
});
|
|
|
|
|
|
function synPoints() {
|
|
|
// 更新 students 数组中的相应学生积分
|
|
|
const studentIndex = students.findIndex(s => s.id === selectedStudent.id);
|
|
|
if (studentIndex !== -1) {
|
|
|
students[studentIndex].points = selectedStudent.points; // 确保 students 数组中的积分也更新
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function repeatCorrectly() {
|
|
|
if (!lock && selectedStudent && !autoPointsAdded) {
|
|
|
selectedStudent.points += 0.5;
|
|
|
synPoints();
|
|
|
autoPointsAdded = true;
|
|
|
lock = true;
|
|
|
updateLocalStorage();
|
|
|
showMessage(`已为 ${selectedStudent.name}加分 0.5。`);
|
|
|
} else if (!selectedStudent) {
|
|
|
showMessage("请先抽取一位学生");
|
|
|
} else {
|
|
|
showMessage("已为该学生加过分");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
function repeatIncorrectly() {
|
|
|
if (!lock && selectedStudent && !autoPointsAdded) {
|
|
|
selectedStudent.points -= 1;
|
|
|
synPoints();
|
|
|
autoPointsAdded = true;
|
|
|
lock = true;
|
|
|
updateLocalStorage();
|
|
|
showMessage(`已为 ${selectedStudent.name} 扣分 1。`);
|
|
|
} else if (!selectedStudent) {
|
|
|
showMessage("请先抽取一位学生");
|
|
|
} else {
|
|
|
showMessage("已为该学生加过分");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let clickEnabled = true; // 用于跟踪点击是否被允许
|
|
|
|
|
|
function scrollStudents() {
|
|
|
document.getElementById('selectedStudent').innerText = '';
|
|
|
const scrollingNamesDiv = document.getElementById('scrollingNames');
|
|
|
scrollingNamesDiv.innerHTML = '';
|
|
|
let index = 0;
|
|
|
|
|
|
clickEnabled = false; // 禁用点击
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
intervalId = setInterval(() => {
|
|
|
scrollingNamesDiv.innerText = students[index].name;
|
|
|
index = (index + 1) % students.length;
|
|
|
}, 100);
|
|
|
|
|
|
setTimeout(() => {
|
|
|
clearInterval(intervalId);
|
|
|
scrollingNamesDiv.innerHTML = '';
|
|
|
if (selectedStudent) {
|
|
|
document.getElementById('selectedStudent').innerText = `${selectedStudent.name} (学号: ${selectedStudent.id})`;
|
|
|
document.getElementById('selectedStudent').style.display = 'none'; // 隐藏
|
|
|
createParticlesFromText(`${selectedStudent.name} (学号:${selectedStudent.id})`);
|
|
|
updateLocalStorage();
|
|
|
}
|
|
|
resolve(); // 完成滚动时解析Promise
|
|
|
clickEnabled = true; // 恢复点击
|
|
|
}, 3000);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function updateLocalStorage() {
|
|
|
localStorage.setItem('selectedStudent', JSON.stringify(selectedStudent));
|
|
|
localStorage.setItem('students', JSON.stringify(students));
|
|
|
localStorage.setItem('autoPointsAdded', autoPointsAdded);
|
|
|
localStorage.setItem('manualPointsUsed', manualPointsUsed);
|
|
|
localStorage.setItem('starLock',starLock);
|
|
|
localStorage.setItem('onClassAdded',onClassAdded);
|
|
|
}
|
|
|
|
|
|
// 初始化星级点击事件监听器
|
|
|
document.querySelectorAll('.rating input').forEach(star => {
|
|
|
star.addEventListener('change', function() {
|
|
|
if (!starLock) { // 检查星星是否已经被点击过
|
|
|
updatePoints(this.value);
|
|
|
} else {
|
|
|
showMessage("每次点名只能点击一次星星进行加分。");
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
// 根据选中的星级为学生加分
|
|
|
function updatePoints(points) {
|
|
|
points = parseFloat(points); // 将点击的星星值转换为整数
|
|
|
if (selectedStudent) {
|
|
|
if (!starLock) { // 检查星星是否已经被点击过
|
|
|
selectedStudent.points += points; // 根据星星值加分
|
|
|
synPoints();
|
|
|
showMessage(`已为 ${selectedStudent.name} 加分 ${points}。`);
|
|
|
starLock = true; // 点击后锁定星星不能再加分
|
|
|
updateLocalStorage();
|
|
|
} else {
|
|
|
showMessage("每次点名只能点击一次星星进行加分。");
|
|
|
}
|
|
|
} else {
|
|
|
showMessage("请先点名一个学生。");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//每次返回页面或者重新点名使星星恢复原始形态
|
|
|
function resetStars() {
|
|
|
document.querySelectorAll('.rating input').forEach(star => {
|
|
|
star.checked = false; // 取消选中状态
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 禁用所有星星选择
|
|
|
function disableStars() {
|
|
|
document.querySelectorAll('.rating input').forEach(star => {
|
|
|
star.disabled = true;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 启用所有星星选择
|
|
|
function enableStars() {
|
|
|
document.querySelectorAll('.rating input').forEach(star => {
|
|
|
star.disabled = false;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function resetAllPoints() {
|
|
|
students.forEach(student => student.points = 0);
|
|
|
localStorage.setItem('students', JSON.stringify(students));
|
|
|
localStorage.removeItem('selectedStudent');
|
|
|
selectedStudent = null;
|
|
|
document.getElementById('selectedStudent').innerText = '';
|
|
|
showMessage("所有积分已重置!");
|
|
|
}
|
|
|
|
|
|
//弹窗
|
|
|
const fileButton = document.getElementById('fileButton');
|
|
|
const uploadModal = document.getElementById('uploadModal');
|
|
|
const closeButton = document.getElementById('closeButton');
|
|
|
|
|
|
fileButton.addEventListener('click',function() {
|
|
|
uploadModal.style.display = 'block'; // 显示模态框
|
|
|
});
|
|
|
|
|
|
closeButton.addEventListener('click', function() {
|
|
|
resetUpload();
|
|
|
uploadModal.style.display = 'none'; // 关闭模态框
|
|
|
});
|
|
|
|
|
|
window.addEventListener('click', function(event) {
|
|
|
if (event.target === uploadModal) {
|
|
|
uploadModal.style.display = 'none'; // 点击模态框外部关闭
|
|
|
}
|
|
|
});
|
|
|
|
|
|
const uploadBox = document.getElementById('uploadBox');
|
|
|
const fileInput = document.getElementById('fileInput');
|
|
|
|
|
|
uploadBox.addEventListener('click', function() {
|
|
|
fileInput.click();
|
|
|
});
|
|
|
|
|
|
uploadBox.addEventListener('dragover', function(event) {
|
|
|
event.preventDefault();
|
|
|
uploadBox.style.borderColor = '#4CAF50';
|
|
|
});
|
|
|
|
|
|
uploadBox.addEventListener('dragleave', function() {
|
|
|
uploadBox.style.borderColor = '#ccc';
|
|
|
});
|
|
|
|
|
|
uploadBox.addEventListener('drop', function(event) {
|
|
|
event.preventDefault();
|
|
|
uploadBox.style.borderColor = '#ccc';
|
|
|
const files = event.dataTransfer.files;
|
|
|
if (files.length > 0) {
|
|
|
const fileName = files[0].name; // 获取文件名
|
|
|
const uploadText = document.getElementById('uploadText');
|
|
|
const excelLogo = '📊'; // Excel 图标,可以使用 emoji,也可以使用图像 URL
|
|
|
|
|
|
// 替换内容
|
|
|
uploadText.innerHTML = `${excelLogo} ${fileName}`;
|
|
|
document.querySelector('.pic').style.display = 'none'; // 隐藏箭头
|
|
|
showMessage('已选择文件: ' + files[0].name);
|
|
|
|
|
|
// 将文件设置到 fileInput 中,以便 OK 按钮可以获取到
|
|
|
fileInput.files = files;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
fileInput.addEventListener('change', function(event) {
|
|
|
const file = event.target.files[0];
|
|
|
if (file) {
|
|
|
const fileName = file.name; // 获取文件名
|
|
|
const uploadText = document.getElementById('uploadText');
|
|
|
const excelLogo = '📊'; // Excel 图标,可以使用 emoji,也可以使用图像 URL
|
|
|
|
|
|
// 替换内容
|
|
|
uploadText.innerHTML = `${excelLogo} ${fileName}`;
|
|
|
document.querySelector('.pic').style.display = 'none'; // 隐藏箭头
|
|
|
showMessage('已选择文件: ' + file.name);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
document.getElementById('cancelButton').addEventListener('click', function() {
|
|
|
resetUpload();
|
|
|
fileInput.value = '';
|
|
|
uploadModal.style.display = 'none'; // 取消时关闭模态框
|
|
|
showMessage('已取消选择');
|
|
|
});
|
|
|
|
|
|
document.getElementById('okButton').addEventListener('click', function() {
|
|
|
const input = document.getElementById('fileInput');
|
|
|
const file = input.files[0]; // 获取选中的文件
|
|
|
|
|
|
if (!file) {
|
|
|
showMessage('请先选择文件');
|
|
|
} else {
|
|
|
showMessage('文件导入成功: ' + file.name);
|
|
|
uploadFile(); // 调用上传文件的函数
|
|
|
uploadModal.style.display = 'none'; // 关闭模态框
|
|
|
}
|
|
|
resetUpload();
|
|
|
});
|
|
|
|
|
|
// 重置上传状态的函数
|
|
|
function resetUpload() {
|
|
|
document.getElementById('fileInput').value = ''; // 清空文件输入
|
|
|
document.getElementById('uploadText').innerHTML = '点击或拖拽文件到此处上传'; // 恢复提示文本
|
|
|
document.querySelector('.pic').style.display = 'block'; // 显示箭头
|
|
|
} |