|  |  | @ -1,3 +1,4 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | <!DOCTYPE html> |  |  |  | <!DOCTYPE html> | 
			
		
	
		
		
			
				
					
					|  |  |  | <html lang="en"> |  |  |  | <html lang="en"> | 
			
		
	
		
		
			
				
					
					|  |  |  | <head> |  |  |  | <head> | 
			
		
	
	
		
		
			
				
					|  |  | @ -17,44 +18,54 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |     <div id="message" style="display:none; position:fixed; top:20px; left:50%; transform:translateX(-50%); background-color:rgba(0, 0, 0, 0.5); color:white; padding:10px; border-radius:5px; white-space:nowrap;">   |  |  |  |     <div id="message" style="display:none; position:fixed; top:20px; left:50%; transform:translateX(-50%); background-color:rgba(0, 0, 0, 0.5); color:white; padding:10px; border-radius:5px; white-space:nowrap;">   | 
			
		
	
		
		
			
				
					
					|  |  |  |     </div> |  |  |  |     </div> | 
			
		
	
		
		
			
				
					
					|  |  |  |     <div class="container">     |  |  |  |     <div class="container">     | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="file-upload">   |  |  |  |         <div class="file-upload"> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |             <button  | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <label class="file-label" for="fileInput" style="cursor: pointer;"> |  |  |  |                 data-tooltip="导入名单" | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 id="fileButton" style="background: none; border: none; cursor: pointer;"> | 
			
		
	
		
		
			
				
					
					|  |  |  |                 <img src="file-icon.svg" alt="选择文件" style="width: 40px; height: 40px;" /> |  |  |  |                 <img src="file-icon.svg" alt="选择文件" style="width: 40px; height: 40px;" /> | 
			
		
	
		
		
			
				
					
					|  |  |  |                 <input type="file" id="fileInput" style="display: none;" onchange="updateFileName()" />  |  |  |  |             </button> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             </label> |  |  |  |             <span id="fileName" style="display: none;">未选择文件</span>  | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             <span id="fileName">未选择文件</span>   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             <button id="uploadButton" onclick="uploadFile()"style="background: none; border: none; padding: 0; cursor: pointer;"> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 <img src="upload-icon.svg" alt="导入" style="width: 40px; height: 40px;" /> |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             </button>   |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         </div>   |  |  |  |         </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         <div class="modal" id="uploadModal">   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             <div class="modal-content">   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 <span class="close-button" id="closeButton">×</span>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 <div class="upload-box" id="uploadBox">   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     <p id="uploadText">点击或拖拽文件到此处上传</p>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     <input type="file" id="fileInput" style="display: none;"/>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     <div class="pic">↓</div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 <div class="button-container">   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     <button id="cancelButton">取消</button>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     <button id="okButton">确定</button>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         </div> | 
			
		
	
		
		
			
				
					
					|  |  |  |      |  |  |  |      | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="button-row">   |  |  |  |         <div class="button-row">   | 
			
		
	
		
		
			
				
					
					|  |  |  |             <button id="drawButton" onclick="startDraw()" disabled>开始点名</button>   |  |  |  |             <button id="drawButton" onclick="startDraw()" style="margin-bottom: 40px;">开始点名</button>   | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         </div>   |  |  |  |         </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="button-row"> |  |  |  |         <div class="button-row"> | 
			
		
	
		
		
			
				
					
					|  |  |  |         <button id="onClass" onclick="onClass()" style="width:20% ;">到场</button>   |  |  |  |         <button id="onClass" onclick="onClass()" style="width:15% ;">到场</button>   | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         </div> |  |  |  |         </div> | 
			
		
	
		
		
			
				
					
					|  |  |  |      |  |  |  |      | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="button-row">   |  |  |  |         <div class="button-row">   | 
			
		
	
		
		
			
				
					
					|  |  |  |             <button id="correctButton" onclick="repeatCorrectly()" disabled>准确重复问题</button> |  |  |  |             <button id="correctButton" onclick="repeatCorrectly()">准确重复问题</button> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <button id="incorrectButton" onclick="repeatIncorrectly()" disabled>重复不准确</button>   |  |  |  |             <button id="incorrectButton" onclick="repeatIncorrectly()">重复不准确</button>   | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         </div>   |  |  |  |         </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |          |  |  |  |          | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="rating"> |  |  |  |         <div class="rating"> | 
			
		
	
		
		
			
				
					
					|  |  |  |             <input type="radio" name="rating" id="5" value="5" /> |  |  |  |             <input type="radio" name="rating" id="5" value="3" /> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <label for="5" data-value="5"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">5</span></label> |  |  |  |             <label for="5" data-value="5"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">3</span></label> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <input type="radio" name="rating" id="4" value="4" /> |  |  |  |             <input type="radio" name="rating" id="4" value="2" /> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <label for="4" data-value="4"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">4</span></label> |  |  |  |             <label for="4" data-value="4"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">2</span></label> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <input type="radio" name="rating" id="3" value="3" /> |  |  |  |             <input type="radio" name="rating" id="3" value="1.5" /> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <label for="3" data-value="3"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">3</span></label> |  |  |  |             <label for="3" data-value="3"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">1.5</span></label> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <input type="radio" name="rating" id="2" value="2" /> |  |  |  |             <input type="radio" name="rating" id="2" value="1" /> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <label for="2" data-value="2"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">2</span></label> |  |  |  |             <label for="2" data-value="2"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">1</span></label> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <input type="radio" name="rating" id="1" value="1" /> |  |  |  |             <input type="radio" name="rating" id="1" value="0.5" /> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             <label for="1" data-value="1"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">1</span></label> |  |  |  |             <label for="1" data-value="1"><i class="icon ion-md-star-outline"></i><span class="star-number hidden">0.5</span></label> | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         </div> |  |  |  |         </div> | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |      |  |  |  |      | 
			
		
	
	
		
		
			
				
					|  |  | @ -63,7 +74,7 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |          |  |  |  |          | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="button-row">   |  |  |  |         <div class="button-row">   | 
			
		
	
		
		
			
				
					
					|  |  |  |             <!--<button id="manualButton" onclick="updatePoints()" disabled>更新积分</button>  --> |  |  |  |             <!--<button id="manualButton" onclick="updatePoints()" disabled>更新积分</button>  --> | 
			
		
	
		
		
			
				
					
					|  |  |  |             <button id="resetButton" onclick="resetAllPoints()">重置所有积分</button>   |  |  |  |             <button id="resetButton" onclick="resetAllPoints()" >重置所有积分</button>   | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         </div>   |  |  |  |         </div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |          |  |  |  |          | 
			
		
	
		
		
			
				
					
					|  |  |  |         <div class="view-student-row">   |  |  |  |         <div class="view-student-row">   | 
			
		
	
	
		
		
			
				
					|  |  | @ -73,423 +84,10 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     <div id="scrollingNames"></div>   |  |  |  |     <div id="scrollingNames"></div>   | 
			
		
	
		
		
			
				
					
					|  |  |  |     <div id="selectedStudent"></div> |  |  |  |     <div id="selectedStudent"></div> | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     <div class="tooltip" id="tooltip"></div> | 
			
		
	
		
		
			
				
					
					|  |  |  |     <canvas id="particleCanvas"></canvas> |  |  |  |     <canvas id="particleCanvas"></canvas> | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.1/xlsx.full.min.js"></script> |  |  |  |     <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.1/xlsx.full.min.js"></script> | 
			
		
	
		
		
			
				
					
					|  |  |  |     <script> |  |  |  |     <script src="mainPage.js"></script> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         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 showMessage(text) {   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             const messageDiv = document.getElementById("message");   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             messageDiv.textContent = text;   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             messageDiv.style.display = "block"; // 显示消息   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             setTimeout(() => {   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 messageDiv.style.display = "none"; // 1秒后隐藏消息   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             }, 1000);   |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         window.onload = function() { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             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'; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             toggleButtons();//按键锁 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |      |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         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 toggleButtons() { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             const buttons = ['drawButton', 'correctButton', 'incorrectButton', 'manualButton']; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             const enable = students.length > 0; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             buttons.forEach(buttonId => { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 document.getElementById(buttonId).disabled = !enable; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         function viewStudentList() { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             window.location.href = 'studentList.html'; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         function startDraw() { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             if (students.length === 0) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 showMessage("请先导入学生名单!"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 隐藏页面元素 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             document.querySelector('.container').classList.add('hidden'); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             // 隐藏“开始点名”按钮 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             //document.getElementById('drawButton').style.display = 'none'; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |      |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             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 = parseInt(points); // 将点击的星星值转换为整数 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             if (selectedStudent) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (!starLock) {  // 检查星星是否已经被点击过 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     selectedStudent.points += points;  // 根据星星值加分 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     synPoints(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     showMessage(`已为 ${selectedStudent.name} 加分 ${points}。`); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     starLock = true;  // 点击后锁定星星 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     updateLocalStorage(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } else { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     showMessage("每次点名只能点击一次星星进行加分。"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             } else { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 showMessage("请先点名一个学生。"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         // 禁用所有星星选择 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         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("所有积分已重置!"); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     </script> |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | </body> |  |  |  | </body> | 
			
		
	
		
		
			
				
					
					|  |  |  | </html> |  |  |  | </html> |