You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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'; // 显示箭头
}