阿幸课堂随机点名

代码功能

这个是一个HTML网页端,简单来说就是可以双击之后运行进行点名。

当然,不局限于课堂点名

代码功能

Excel 导入增强:

增加了列选择器,可以指定从哪一列读取学生姓名

增加了起始行选择器,可以跳过标题行或其他非学生数据行

自动检测功能:尝试识别可能包含姓名的列并自动选择

一键清空功能:

在学生列表上方添加了 "清空名单" 按钮

点击后会提示确认,防止误操作

用户体验优化:

导入 Excel 后显示导入选项,导入完成后自动隐藏

优化了通知提示效果

保持了原有随机点名的核心功能不变

界面截图

源码

cpp 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>阿幸随机点名抽问</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#4F46E5',
                        secondary: '#EC4899',
                        accent: '#8B5CF6',
                        neutral: '#F3F4F6',
                        "neutral-dark": '#1F2937',
                    },
                    fontFamily: {
                        cute: ['"Comic Sans MS"', '"Marker Felt"', 'Arial', 'sans-serif'],
                    },
                    animation: {
                        'bounce-slow': 'bounce 3s infinite',
                    }
                },
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .text-shadow {
                text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
            }
            .bg-cute {
                background-image: radial-gradient(circle at 25% 25%, rgba(255, 220, 220, 0.8) 0%, transparent 50%),
                                  radial-gradient(circle at 75% 75%, rgba(220, 220, 255, 0.8) 0%, transparent 50%);
                background-color: #FFF5F7;
            }
            .name-container {
                perspective: 1000px;
            }
            .name-card {
                transform-style: preserve-3d;
                transition: transform 0.5s;
            }
            .name-card:hover {
                transform: translateY(-5px) rotateX(5deg);
            }
        }
    </style>
</head>
<body class="min-h-screen bg-cute flex flex-col items-center justify-between p-4 md:p-8">
    <!-- 顶部标题区域 -->
    <header class="w-full text-center mb-8">
        <h1 class="text-[clamp(2rem,5vw,3.5rem)] font-cute font-bold text-primary text-shadow animate-bounce-slow">
            <i class="fa fa-star text-yellow-400"></i>
            阿幸随机点名抽问
            <i class="fa fa-star text-yellow-400"></i>
        </h1>
        <p class="text-gray-600 mt-2">公平、随机、有趣的课堂互动工具</p>
    </header>

    <!-- 主要内容区域 -->
    <main class="flex-1 w-full max-w-4xl flex flex-col items-center justify-center">
        <!-- 名字显示区域 -->
        <div class="name-container w-full bg-white rounded-2xl shadow-lg p-6 md:p-10 mb-8 transform transition-all duration-300 hover:shadow-xl">
            <div class="name-card bg-gradient-to-br from-pink-100 to-purple-100 rounded-xl p-8 text-center">
                <p id="displayedName" class="text-[clamp(2.5rem,8vw,5rem)] font-black text-neutral-dark transition-all duration-500">
                    准备开始
                </p>
            </div>
        </div>

        <!-- 控制面板 -->
        <div class="w-full bg-white rounded-xl shadow-md p-6 md:p-8">
            <div class="flex flex-col md:flex-row items-center justify-between gap-6">
                <!-- 速度控制滑块 -->
                <div class="w-full md:w-1/3">
                    <label for="speedControl" class="block text-gray-700 font-medium mb-2 flex items-center">
                        <i class="fa fa-tachometer text-accent mr-2"></i>
                        滚动速度
                    </label>
                    <input type="range" id="speedControl" min="50" max="500" value="200" 
                           class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
                    <div class="flex justify-between text-xs text-gray-500 mt-1">
                        <span>慢</span>
                        <span>快</span>
                    </div>
                </div>

                <!-- 开始/停止按钮 -->
                <div class="w-full md:w-auto flex justify-center md:justify-end">
                    <button id="toggleButton" class="bg-primary hover:bg-primary/90 text-white font-bold py-3 px-8 rounded-full shadow-lg transform transition-all duration-300 hover:scale-105 active:scale-95 flex items-center">
                        <i class="fa fa-play mr-2"></i>
                        <span>开始点名</span>
                    </button>
                </div>
            </div>
        </div>

        <!-- 学生名单管理 -->
        <div class="w-full mt-8 bg-white rounded-xl shadow-md p-6 md:p-8">
            <h2 class="text-xl font-bold text-gray-800 mb-4 flex items-center">
                <i class="fa fa-users text-secondary mr-2"></i>
                学生名单管理
            </h2>
            
            <div class="flex flex-col md:flex-row gap-4">
                <!-- 文本导入 -->
                <div class="w-full md:w-1/2">
                    <label for="nameList" class="block text-gray-700 font-medium mb-2">
                        输入学生姓名(用逗号、空格或换行分隔)
                    </label>
                    <textarea id="nameList" rows="4" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all" placeholder="张三, 李四, 王五..."></textarea>
                    <button id="importNames" class="mt-2 bg-accent hover:bg-accent/90 text-white font-medium py-2 px-4 rounded-lg shadow transition-all duration-200 flex items-center">
                        <i class="fa fa-upload mr-1"></i> 导入名单
                    </button>
                </div>
                
                <!-- Excel导入 -->
                <div class="w-full md:w-1/2">
                    <label class="block text-gray-700 font-medium mb-2">
                        或从Excel文件导入
                    </label>
                    <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-primary transition-colors cursor-pointer" id="excelDropArea">
                        <input type="file" id="excelFile" accept=".xlsx,.xls" class="hidden">
                        <i class="fa fa-file-excel-o text-3xl text-gray-400 mb-2"></i>
                        <p class="text-gray-500">拖放Excel文件到此处<br>或点击选择文件</p>
                    </div>
                    <div id="excelFileName" class="mt-2 text-sm text-gray-600 hidden"></div>
                    
                    <!-- Excel导入选项 -->
                    <div id="excelOptions" class="mt-4 grid grid-cols-2 gap-4 hidden">
                        <div>
                            <label for="excelColumn" class="block text-gray-700 text-sm font-medium mb-1">
                                姓名列选择
                            </label>
                            <select id="excelColumn" class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary focus:border-primary">
                                <option value="A">A列</option>
                                <option value="B">B列</option>
                                <option value="C">C列</option>
                                <option value="D">D列</option>
                                <option value="E">E列</option>
                                <option value="F">F列</option>
                            </select>
                        </div>
                        <div>
                            <label for="excelStartRow" class="block text-gray-700 text-sm font-medium mb-1">
                                起始行号
                            </label>
                            <input type="number" id="excelStartRow" min="1" value="1" 
                                   class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary focus:border-primary">
                        </div>
                    </div>
                    
                    <button id="importExcel" class="mt-2 bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg shadow transition-all duration-200 flex items-center hidden">
                        <i class="fa fa-check mr-1"></i> 确认导入Excel
                    </button>
                </div>
            </div>
            
            <!-- 学生列表显示 -->
            <div class="mt-6">
                <div class="flex justify-between items-center mb-3">
                    <h3 class="text-lg font-medium text-gray-700 flex items-center">
                        <i class="fa fa-list-ul text-primary mr-2"></i>
                        已导入学生 (<span id="studentCount">0</span>)
                    </h3>
                    <button id="clearStudents" class="text-red-500 hover:text-red-600 text-sm font-medium flex items-center">
                        <i class="fa fa-trash mr-1"></i> 清空名单
                    </button>
                </div>
                <div id="studentList" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2 max-h-40 overflow-y-auto p-2 bg-gray-50 rounded-lg">
                    <div class="text-gray-400 col-span-full text-center py-4">
                        暂无学生名单
                    </div>
                </div>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="w-full text-center text-gray-500 text-sm mt-8">
        <p>双击此页面即可离线使用 | 点击右上角可保存为应用</p>
    </footer>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // DOM元素
            const displayedName = document.getElementById('displayedName');
            const toggleButton = document.getElementById('toggleButton');
            const speedControl = document.getElementById('speedControl');
            const nameList = document.getElementById('nameList');
            const importNames = document.getElementById('importNames');
            const excelFile = document.getElementById('excelFile');
            const excelDropArea = document.getElementById('excelDropArea');
            const excelFileName = document.getElementById('excelFileName');
            const excelOptions = document.getElementById('excelOptions');
            const importExcel = document.getElementById('importExcel');
            const excelColumn = document.getElementById('excelColumn');
            const excelStartRow = document.getElementById('excelStartRow');
            const studentList = document.getElementById('studentList');
            const studentCount = document.getElementById('studentCount');
            const clearStudents = document.getElementById('clearStudents');

            // 状态变量
            let students = [];
            let isRolling = false;
            let rollInterval;
            let rollSpeed = 200; // 默认速度,值越小越快
            let selectedName = null;
            let excelData = null;

            // 处理学生名单导入
            importNames.addEventListener('click', function() {
                const text = nameList.value.trim();
                if (!text) {
                    alert('请输入学生姓名');
                    return;
                }
                
                // 支持逗号、空格、换行等多种分隔符
                students = text.split(/[,\s\n]+/).filter(name => name.trim() !== '');
                
                updateStudentList();
                resetRollState();
                showNotification(`成功导入 ${students.length} 名学生`);
            });

            // 处理Excel文件导入
            excelFile.addEventListener('change', handleExcelFile);
            excelDropArea.addEventListener('click', () => excelFile.click());
            
            // 拖放功能
            excelDropArea.addEventListener('dragover', function(e) {
                e.preventDefault();
                this.classList.add('border-primary', 'bg-primary/5');
            });
            
            excelDropArea.addEventListener('dragleave', function() {
                this.classList.remove('border-primary', 'bg-primary/5');
            });
            
            excelDropArea.addEventListener('drop', function(e) {
                e.preventDefault();
                this.classList.remove('border-primary', 'bg-primary/5');
                
                if (e.dataTransfer.files.length) {
                    excelFile.files = e.dataTransfer.files;
                    handleExcelFile();
                }
            });

            function handleExcelFile() {
                const file = excelFile.files[0];
                if (!file) return;
                
                if (!file.name.match(/\.(xlsx|xls)$/i)) {
                    alert('请选择Excel文件 (.xlsx 或 .xls)');
                    return;
                }
                
                excelFileName.textContent = `已选择: ${file.name}`;
                excelFileName.classList.remove('hidden');
                excelOptions.classList.remove('hidden');
                importExcel.classList.remove('hidden');
                
                const reader = new FileReader();
                reader.onload = function(e) {
                    try {
                        const data = new Uint8Array(e.target.result);
                        const workbook = XLSX.read(data, { type: 'array' });
                        const firstSheetName = workbook.SheetNames[0];
                        const worksheet = workbook.Sheets[firstSheetName];
                        
                        // 保存Excel数据供后续处理
                        excelData = worksheet;
                        
                        // 尝试自动检测姓名列
                        autoDetectNameColumn(worksheet);
                        
                    } catch (error) {
                        console.error('Excel处理错误:', error);
                        alert('处理Excel文件时出错,请确保文件格式正确');
                    }
                };
                reader.readAsArrayBuffer(file);
            }
            
            // 自动检测姓名列
            function autoDetectNameColumn(worksheet) {
                const nameKeywords = ['姓名', '名字', '学生姓名', 'name', 'student'];
                const headerRow = XLSX.utils.sheet_to_json(worksheet, { header: 1, range: 0, raw: false })[0] || [];
                
                for (let i = 0; i < headerRow.length; i++) {
                    const cellValue = headerRow[i] || '';
                    const cellValueLower = cellValue.toString().toLowerCase();
                    
                    if (nameKeywords.some(kw => cellValueLower.includes(kw))) {
                        // 将列索引转换为字母 (0->A, 1->B, ...)
                        const columnLetter = String.fromCharCode(65 + i);
                        excelColumn.value = columnLetter;
                        return;
                    }
                }
                
                // 默认选择A列
                excelColumn.value = 'A';
            }
            
            // 确认导入Excel
            importExcel.addEventListener('click', function() {
                if (!excelData) {
                    alert('请先选择Excel文件');
                    return;
                }
                
                const column = excelColumn.value;
                const startRow = parseInt(excelStartRow.value) || 1;
                
                try {
                    // 提取指定列的数据
                    const range = XLSX.utils.decode_range(excelData['!ref'] || 'A1:A1');
                    const maxRow = range.e.r;
                    
                    students = [];
                    for (let row = startRow; row <= maxRow; row++) {
                        const cellAddress = `${column}${row}`;
                        const cell = excelData[cellAddress];
                        
                        if (cell && cell.v && cell.v.toString().trim() !== '') {
                            students.push(cell.v.toString().trim());
                        }
                    }
                    
                    if (students.length === 0) {
                        alert('未能从Excel文件中提取到有效姓名,请检查列选择和起始行');
                        return;
                    }
                    
                    updateStudentList();
                    resetRollState();
                    showNotification(`成功从Excel导入 ${students.length} 名学生`);
                    
                    // 导入后隐藏选项
                    excelOptions.classList.add('hidden');
                    importExcel.classList.add('hidden');
                    
                } catch (error) {
                    console.error('Excel导入错误:', error);
                    alert('导入Excel数据时出错,请检查设置');
                }
            });

            // 更新学生列表显示
            function updateStudentList() {
                studentList.innerHTML = '';
                
                if (students.length === 0) {
                    studentList.innerHTML = `
                        <div class="text-gray-400 col-span-full text-center py-4">
                            暂无学生名单
                        </div>
                    `;
                    studentCount.textContent = '0';
                    return;
                }
                
                students.forEach(name => {
                    const nameEl = document.createElement('div');
                    nameEl.className = 'bg-white border border-gray-200 rounded p-2 text-center text-sm hover:bg-primary/5 transition-colors';
                    nameEl.textContent = name;
                    nameEl.dataset.name = name;
                    
                    studentList.appendChild(nameEl);
                });
                
                studentCount.textContent = students.length;
            }

            // 开始/停止点名
            toggleButton.addEventListener('click', function() {
                if (students.length === 0) {
                    alert('请先导入学生名单');
                    return;
                }
                
                if (isRolling) {
                    stopRolling();
                } else {
                    startRolling();
                }
            });

            // 开始滚动
            function startRolling() {
                isRolling = true;
                toggleButton.innerHTML = '<i class="fa fa-stop mr-2"></i><span>停止点名</span>';
                toggleButton.classList.remove('bg-primary');
                toggleButton.classList.add('bg-red-500', 'hover:bg-red-600');
                
                // 重置之前的选中状态
                displayedName.classList.remove('text-primary', 'font-black', 'scale-110');
                
                // 开始滚动
                rollInterval = setInterval(() => {
                    const randomIndex = Math.floor(Math.random() * students.length);
                    displayedName.textContent = students[randomIndex];
                    
                    // 添加动画效果
                    displayedName.classList.add('animate-pulse');
                    setTimeout(() => {
                        displayedName.classList.remove('animate-pulse');
                    }, 50);
                    
                }, 600 - rollSpeed); // 反转速度值,使滑块右侧表示更快
            }

            // 停止滚动
            function stopRolling() {
                clearInterval(rollInterval);
                isRolling = false;
                toggleButton.innerHTML = '<i class="fa fa-play mr-2"></i><span>开始点名</span>';
                toggleButton.classList.remove('bg-red-500', 'hover:bg-red-600');
                toggleButton.classList.add('bg-primary', 'hover:bg-primary/90');
                
                // 突出显示选中的名字
                selectedName = displayedName.textContent;
                displayedName.classList.add('text-primary', 'font-black', 'scale-110', 'transition-transform', 'duration-500');
                
                // 高亮学生列表中的选中项
                highlightSelectedStudent();
            }

            // 高亮选中的学生
            function highlightSelectedStudent() {
                const nameElements = studentList.querySelectorAll('[data-name]');
                nameElements.forEach(el => {
                    if (el.dataset.name === selectedName) {
                        el.classList.add('bg-primary/10', 'border-primary', 'font-bold');
                        el.scrollIntoView({ behavior: 'smooth', block: 'center' });
                    } else {
                        el.classList.remove('bg-primary/10', 'border-primary', 'font-bold');
                    }
                });
            }

            // 重置滚动状态
            function resetRollState() {
                if (isRolling) {
                    clearInterval(rollInterval);
                    isRolling = false;
                }
                
                displayedName.textContent = '准备开始';
                displayedName.classList.remove('text-primary', 'font-black', 'scale-110');
                toggleButton.innerHTML = '<i class="fa fa-play mr-2"></i><span>开始点名</span>';
                toggleButton.classList.remove('bg-red-500', 'hover:bg-red-600');
                toggleButton.classList.add('bg-primary', 'hover:bg-primary/90');
                
                // 清除学生列表中的高亮
                const nameElements = studentList.querySelectorAll('[data-name]');
                nameElements.forEach(el => {
                    el.classList.remove('bg-primary/10', 'border-primary', 'font-bold');
                });
            }

            // 速度控制
            speedControl.addEventListener('input', function() {
                rollSpeed = parseInt(this.value);
                
                // 如果正在滚动,更新速度
                if (isRolling) {
                    clearInterval(rollInterval);
                    startRolling();
                }
            });

            // 显示通知
            function showNotification(message) {
                // 创建通知元素
                const notification = document.createElement('div');
                notification.className = 'fixed top-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg transform transition-all duration-300 translate-x-full opacity-0 z-50';
                notification.textContent = message;
                
                // 添加到页面
                document.body.appendChild(notification);
                
                // 显示通知
                setTimeout(() => {
                    notification.classList.remove('translate-x-full', 'opacity-0');
                }, 10);
                
                // 自动隐藏
                setTimeout(() => {
                    notification.classList.add('translate-x-full', 'opacity-0');
                    setTimeout(() => {
                        document.body.removeChild(notification);
                    }, 300);
                }, 3000);
            }

            // 清空学生名单
            clearStudents.addEventListener('click', function() {
                if (students.length === 0) return;
                
                if (confirm('确定要清空当前学生名单吗?')) {
                    students = [];
                    updateStudentList();
                    resetRollState();
                    showNotification('已清空学生名单');
                }
            });

            // 键盘快捷键
            document.addEventListener('keydown', function(e) {
                // 空格键控制开始/停止
                if (e.code === 'Space') {
                    e.preventDefault();
                    if (students.length > 0) {
                        toggleButton.click();
                    }
                }
            });

            // 示例数据
            nameList.value = "张三、李四、王五、赵六、钱七、孙八、周九、吴十、郑十一、王十二";
        });
    </script>
</body>
</html>
    

源码文件

迅雷下载

相关推荐
小小小新人12123几秒前
C语言 ATM (4)
c语言·开发语言·算法
Two_brushes.8 分钟前
【linux网络】网络编程全流程详解:从套接字基础到 UDP/TCP 通信实战
linux·开发语言·网络·tcp/udp
小白学大数据11 分钟前
R语言爬虫实战:如何爬取分页链接并批量保存
开发语言·爬虫·信息可视化·r语言
争不过朝夕,又念着往昔14 分钟前
Go语言反射机制详解
开发语言·后端·golang
Azxcc025 分钟前
C++异步编程入门
开发语言·c++
Biaobiaone29 分钟前
Java中的生产消费模型解析
java·开发语言
我命由我123451 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js
Jokerator1 小时前
深入解析JavaScript获取元素宽度的多种方式
javascript·css
海天胜景1 小时前
vue3 当前页面方法暴露
前端·javascript·vue.js
特立独行的猫a1 小时前
11款常用C++在线编译与运行平台推荐与对比
java·开发语言·c++