|  |  |  | @ -0,0 +1,528 @@ | 
			
		
	
		
			
				
					|  |  |  |  | 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'; // 显示箭头
 | 
			
		
	
		
			
				
					|  |  |  |  | } |