班级小星星系统(HTML单文件,代码附后面)

课堂管理神器来了!用「小星星奖励系统」点燃学生积极性

还在为课堂氛围不够活跃、学生参与度低而发愁?今天给各位老师推荐一款超实用的课堂管理工具——班级小星星奖励系统,用趣味化的星星激励机制,轻松培养学生的良好学习习惯,让课堂互动焕新升级!

一、系统核心:用“星星”激活课堂动力

这款系统专为教师设计,核心是通过“星星奖励”正向引导学生:课上积极发言、作业完成出色、小组合作优秀……都能获得星星奖励。看得见的荣誉积累,能让学生从“要我学”变成“我要挣星星”,学习主动性直接拉满!

二、4大核心功能,覆盖课堂管理全场景

1. 学生管理:灵活高效,一键搞定

– 支持单个添加或批量导入学生,新班级组建也能快速上手;

– 随时编辑学生信息,或删除错误记录,管理更灵活;

– 学生表现好时,点击卡片上的“+”就能即时奖励星星,操作超简单。

2. 小组管理:培养协作,集体共赢

– 切换“小组视图”,就能以小组为单位进行集体奖励;

– 可自由添加、编辑小组,适配不同课堂活动需求;

– 小组合作优秀?一键给全组加星,强化团队协作意识。

3. 星星管理:数据清晰,实时追踪

– 本节星星:实时显示当前课堂的奖励情况,即时反馈课堂效果;

– 今日星星:自动汇总当天星星总数,一天学习成果一目了然;

– 累计星星:注册后解锁,记录学生长期进步,成就感翻倍。

4. 数据查看:可视化排行,激励更有效

– 点击“排序查看”,即可生成学生排行榜;

– 支持按“本节/今日/累计星星”切换排序标准,谁是“星星达人”一眼便知;

– 同步功能一键开启:将本节星星自动累加到今日和累计数据中,本节星星同步后清零,数据不混乱。

三、4步上手!小白也能轻松操作

1. 添加学生:点击右上角“+”,选择“单个添加”或“批量导入”,输入姓名确认即可;

2. 奖励星星:学生视图点学生卡片“+”,小组视图点小组卡片“+”,即时奖励不延迟;

3. 查看数据:点击“排序查看”,按需选择排序标准,掌握学生表现;

4. 数据同步:课后点击“同步”,本节星星自动转入今日/累计数据,为下节课清零准备。

四、注册说明&注意事项

– 未注册版本:仅支持使用“本节星星”功能,满足基础课堂需求;

– 注册解锁更多:点击“注册”获取设备ID,输入注册码后,即可解锁“累计星星”功能,长期追踪学生进步;

– 关键提醒:

1. 批量导入学生时,需“每行输入一个姓名”,格式更规范;

2. 删除学生/小组、清空小组星星操作“不可恢复”,请谨慎点击;

3. 同步操作会自动累加数据,建议课后统一完成,避免数据重复。

五、界面设计:简洁直观,上手无压力

– 学生视图:卡片式展示每位学生的星星数,谁多谁少清晰可见;

– 小组视图:以小组为单位呈现星星情况,集体荣誉可视化;

– 排行榜:按星星数量从高到低排序,激发学生的竞争意识与上进心。

从此,课堂管理不用愁,学生积极性不用催!用「班级小星星奖励系统」,让每一份努力都被看见,每一次进步都有奖励。

如有使用问题或需要注册码,可随时联系系统管理员获取帮助。期待这款工具能成为各位老师的课堂好帮手,和大家一起打造更有活力的学习氛围!

源代码如下:

<!DOCTYPE html>
<html lang=”zh-CN”>
<head>
    <meta charset=”UTF-8″>
    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
    <title>班级小星星奖励系统</title>
    <link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css”>
    <style>
        :root {
            –primary-color: #4a6bdf;
            –secondary-color: #f5c542;
            –danger-color: #e74c3c;
            –success-color: #2ecc71;
            –light-color: #f9f9f9;
            –dark-color: #333;
            –border-radius: 8px;
            –box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background-color: #f0f2f5;
            color: var(–dark-color);
            line-height: 1.6;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
            padding: 15px;
            background-color: white;
            border-radius: var(–border-radius);
            box-shadow: var(–box-shadow);
        }
        
        .header-text {
            flex: 1;
        }
        
        h1 {
            color: var(–primary-color);
            margin-bottom: 5px;
            font-size: 1.5rem;
        }
        
        .subtitle {
            color: #666;
            font-size: 0.9rem;
        }
        
        .header-actions {
            display: flex;
            gap: 10px;
        }
        
        .header-actions button {
            background-color: var(–primary-color);
            color: white;
            border: none;
            border-radius: 50%;
            width: 45px;
            height: 45px;
            font-size: 1.2rem;
            cursor: pointer;
            box-shadow: var(–box-shadow);
            transition: all 0.3s;
        }
        
        .header-actions button:hover {
            background-color: #3a5bd0;
            transform: scale(1.05);
        }
        
        .header-actions .registered {
            background-color: var(–success-color);
        }
        
        .header-actions .active {
            background-color: var(–success-color);
        }
        
        .header-actions .clear-btn {
            background-color: var(–danger-color);
        }
        
        .header-actions .clear-btn:hover {
            background-color: #c0392b;
        }
        
        .view-toggle {
            display: flex;
            margin-bottom: 15px;
            border-radius: var(–border-radius);
            overflow: hidden;
            border: 1px solid var(–primary-color);
        }
        
        .view-toggle-btn {
            flex: 1;
            padding: 10px;
            text-align: center;
            background-color: white;
            color: var(–primary-color);
            cursor: pointer;
            transition: all 0.3s;
            font-weight: 600;
            font-size: 0.9rem;
        }
        
        .view-toggle-btn.active {
            background-color: var(–primary-color);
            color: white;
        }
        
        .students-grid, .groups-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 15px;
        }
        
        .student-card, .group-card {
            background-color: white;
            border-radius: var(–border-radius);
            padding: 15px;
            box-shadow: var(–box-shadow);
            transition: transform 0.3s;
            position: relative;
            min-height: 150px;
            display: flex;
            flex-direction: column;
        }
        
        .student-card:hover, .group-card:hover {
            transform: translateY(-3px);
        }
        
        .student-name, .group-name {
            font-size: 1.4rem;
            font-weight: 600;
            margin-bottom: 10px;
            color: var(–primary-color);
            text-align: center;
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        .today-stars-visual, .group-stars-visual {
            display: flex;
            justify-content: center;
            margin-bottom: 10px;
            flex-wrap: wrap;
            gap: 3px;
        }
        
        .star-icon-small {
            color: var(–secondary-color);
            font-size: 1.2rem;
        }
        
        .stars-container, .group-stars-container {
            display: flex;
            justify-content: center;
            margin-bottom: 10px;
        }
        
        .today-stars, .total-stars, .group-stars {
            text-align: center;
        }
        
        .stars-count, .group-stars-count {
            font-size: 1.8rem;
            font-weight: 700;
            color: var(–secondary-color);
            margin-top: 5px;
        }
        
        .stars-label {
            font-size: 0.8rem;
            color: #666;
        }
        
        .stars-controls, .group-stars-controls {
            display: flex;
            gap: 8px;
            margin-bottom: 5px;
        }
        
        .stars-controls button, .group-stars-controls button {
            flex: 1;
            padding: 6px;
            font-size: 1.2rem;
            background-color: var(–primary-color);
            color: white;
            border: none;
            border-radius: var(–border-radius);
            cursor: pointer;
            transition: background-color 0.3s;
        }
        
        .stars-controls button:hover, .group-stars-controls button:hover {
            background-color: #3a5bd0;
        }
        
        .stars-controls button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
        
        .card-actions, .group-card-actions {
            position: absolute;
            top: 10px;
            right: 10px;
        }
        
        .edit-btn, .group-edit-btn {
            background: none;
            border: none;
            color: #666;
            cursor: pointer;
            font-size: 1rem;
            transition: color 0.3s;
            width: auto;
            padding: 5px;
        }
        
        .edit-btn:hover, .group-edit-btn:hover {
            color: var(–primary-color);
        }
        
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 100;
            justify-content: center;
            align-items: center;
        }
        
        .modal-content {
            background-color: white;
            padding: 25px;
            border-radius: var(–border-radius);
            width: 90%;
            max-width: 450px;
            box-shadow: var(–box-shadow);
        }
        
        .modal h2 {
            margin-bottom: 15px;
            color: var(–primary-color);
            text-align: center;
            font-size: 1.3rem;
        }
        
        .modal-buttons {
            display: flex;
            gap: 10px;
            margin-top: 15px;
        }
        
        .modal-buttons button {
            flex: 1;
            padding: 8px;
            border: none;
            border-radius: var(–border-radius);
            cursor: pointer;
            font-weight: 600;
            transition: background-color 0.3s;
            font-size: 0.9rem;
        }
        
        .modal-buttons .primary {
            background-color: var(–primary-color);
            color: white;
        }
        
        .modal-buttons .primary:hover {
            background-color: #3a5bd0;
        }
        
        .modal-buttons .secondary {
            background-color: #e0e0e0;
            color: #333;
        }
        
        .modal-buttons .secondary:hover {
            background-color: #d0d0d0;
        }
        
        .input-group {
            margin-bottom: 12px;
        }
        
        input, textarea, button {
            padding: 8px 12px;
            border: 1px solid #ddd;
            border-radius: var(–border-radius);
            font-size: 0.9rem;
            width: 100%;
        }
        
        .tabs {
            display: flex;
            margin-bottom: 15px;
            border-bottom: 1px solid #ddd;
        }
        
        .tab {
            padding: 8px 15px;
            cursor: pointer;
            border-bottom: 3px solid transparent;
            font-size: 0.9rem;
        }
        
        .tab.active {
            border-bottom: 3px solid var(–primary-color);
            color: var(–primary-color);
            font-weight: 600;
        }
        
        .tab-content {
            display: none;
        }
        
        .tab-content.active {
            display: block;
        }
        
        .empty-state {
            text-align: center;
            padding: 30px;
            color: #666;
            background-color: white;
            border-radius: var(–border-radius);
            box-shadow: var(–box-shadow);
        }
        
        .unregistered-notice {
            background-color: #fff3cd;
            border: 1px solid #ffeaa7;
            color: #856404;
            padding: 8px 12px;
            border-radius: var(–border-radius);
            margin-bottom: 15px;
            text-align: center;
            font-size: 0.9rem;
        }
        
        .sort-list {
            max-height: 350px;
            overflow-y: auto;
            margin-bottom: 15px;
        }
        
        .sort-list-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px 12px;
            border-bottom: 1px solid #eee;
        }
        
        .sort-list-item:last-child {
            border-bottom: none;
        }
        
        .sort-list-item .student-info {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .sort-list-item .student-name {
            font-size: 1rem;
            margin: 0;
            text-align: left;
        }
        
        .sort-list-item .stars-count {
            font-size: 1.2rem;
        }
        
        .sort-tabs {
            display: flex;
            margin-bottom: 12px;
            border-radius: var(–border-radius);
            overflow: hidden;
            border: 1px solid var(–primary-color);
        }
        
        .sort-tab {
            flex: 1;
            padding: 8px;
            text-align: center;
            background-color: white;
            color: var(–primary-color);
            cursor: pointer;
            transition: all 0.3s;
            font-size: 0.9rem;
        }
        
        .sort-tab.active {
            background-color: var(–primary-color);
            color: white;
        }
        
        @media (max-width: 768px) {
            header {
                flex-direction: column;
                gap: 12px;
                text-align: center;
            }
            
            .header-actions {
                width: 100%;
                justify-content: center;
                flex-wrap: wrap;
            }
            
            .students-grid, .groups-grid {
                grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
            }
            
            .student-name, .group-name {
                font-size: 1.2rem;
            }
        }
        
        @media (max-width: 480px) {
            .students-grid, .groups-grid {
                grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
            }
            
            .student-card, .group-card {
                padding: 12px;
            }
        }
    </style>
</head>
<body>
    <div class=”container”>
        <header>
            <div class=”header-text”>
                <h1>班级小星星奖励系统</h1>
                <p class=”subtitle”>记录和奖励学生们的优秀表现</p>
            </div>
            <div class=”header-actions”>
                <button class=”sync-btn” id=”syncStars” title=”同步并清空本节星星”><i class=”fas fa-sync-alt”></i></button>
                <button class=”sort-btn” id=”openSortModal” title=”排序查看”><i class=”fas fa-sort-amount-down”></i></button>
                <button class=”group-btn” id=”toggleView” title=”切换到小组视图”><i class=”fas fa-users”></i></button>
                <button class=”add-btn” id=”openAddModal” title=”添加学生/小组”><i class=”fas fa-plus”></i></button>
                <button class=”clear-btn” id=”clearGroupStars” title=”清空小组星星”><i class=”fas fa-eraser”></i></button>
                <button class=”register-btn” id=”openRegisterModal” title=”注册软件”><i class=”fas fa-key”></i></button>
            </div>
        </header>

        <div id=”unregisteredNotice” class=”unregistered-notice” style=”display: none;”>
            <i class=”fas fa-exclamation-triangle”></i> 软件未注册,累计星星功能已禁用。请注册后使用完整功能。
        </div>

        <div class=”view-toggle”>
            <div class=”view-toggle-btn active” data-view=”students”>学生视图</div>
            <div class=”view-toggle-btn” data-view=”groups”>小组视图</div>
        </div>

        <div id=”studentsView” class=”view-content”>
            <div id=”studentsContainer” class=”students-grid”></div>
        </div>

        <div id=”groupsView” class=”view-content” style=”display: none;”>
            <div id=”groupsContainer” class=”groups-grid”></div>
        </div>
    </div>

    <!– 模态框部分保持不变 –>
    <div id=”addModal” class=”modal”>
        <div class=”modal-content”>
            <h2 id=”addModalTitle”>添加学生</h2>
            <div class=”tabs”>
                <div class=”tab active” data-tab=”single”>单个添加</div>
                <div class=”tab” data-tab=”batch”>批量导入</div>
            </div>
            <div class=”tab-content active” id=”single-tab”>
                <div class=”input-group”>
                    <input type=”text” id=”studentName” placeholder=”输入学生姓名”>
                </div>
                <div class=”modal-buttons”>
                    <button class=”primary” id=”addStudent”>添加</button>
                    <button class=”secondary” id=”closeAddModal”>取消</button>
                </div>
            </div>
            <div class=”tab-content” id=”batch-tab”>
                <div class=”input-group”>
                    <textarea id=”importStudents” placeholder=”批量导入学生,每行一个姓名”></textarea>
                </div>
                <div class=”modal-buttons”>
                    <button class=”primary” id=”importBtn”>导入</button>
                    <button class=”secondary” id=”closeImportModal”>取消</button>
                </div>
            </div>
        </div>
    </div>

    <div id=”editModal” class=”modal”>
        <div class=”modal-content”>
            <h2 id=”editModalTitle”>编辑学生信息</h2>
            <div class=”input-group”>
                <input type=”text” id=”editStudentName” placeholder=”学生姓名”>
            </div>
            <div class=”modal-buttons”>
                <button class=”primary” id=”saveEdit”>保存</button>
                <button class=”secondary” id=”deleteStudent”>删除学生</button>
                <button class=”secondary” id=”cancelEdit”>取消</button>
            </div>
        </div>
    </div>

    <div id=”editGroupModal” class=”modal”>
        <div class=”modal-content”>
            <h2>编辑小组信息</h2>
            <div class=”input-group”>
                <input type=”text” id=”editGroupName” placeholder=”小组名称”>
            </div>
            <div class=”modal-buttons”>
                <button class=”primary” id=”saveGroupEdit”>保存</button>
                <button class=”secondary” id=”deleteGroup”>删除小组</button>
                <button class=”secondary” id=”cancelGroupEdit”>取消</button>
            </div>
        </div>
    </div>

    <div id=”sortModal” class=”modal”>
        <div class=”modal-content”>
            <h2>学生排行榜</h2>
            <div class=”sort-tabs”>
                <div class=”sort-tab active” data-sort=”session”>本节星星</div>
                <div class=”sort-tab” data-sort=”today”>今日星星</div>
                <div class=”sort-tab” data-sort=”total”>累计星星</div>
            </div>
            <div class=”sort-list” id=”sortList”></div>
            <div class=”modal-buttons”>
                <button class=”secondary” id=”closeSortModal”>关闭</button>
            </div>
        </div>
    </div>

    <div id=”registerModal” class=”modal”>
        <div class=”modal-content”>
            <h2>软件注册</h2>
            <div class=”input-group”>
                <label>设备ID:</label>
                <input type=”text” id=”deviceId” readonly>
            </div>
            <div class=”input-group”>
                <label>注册码:</label>
                <input type=”text” id=”registerCode” placeholder=”请输入注册码”>
            </div>
            <div class=”modal-buttons”>
                <button class=”primary” id=”registerBtn”>注册</button>
                <button class=”secondary” id=”closeRegisterModal”>取消</button>
            </div>
            <div style=”margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: var(–border-radius);”>
                <p style=”font-size: 0.9rem; color: #666;”>注册说明: 请联系639获取注册码。注册后可解锁累计星星功能。</p>
            </div>
        </div>
    </div>

    <script>
       let students=[];let groups=[];let currentEditId=null;let currentEditGroupId=null;let currentSortType=’session’;let isRegistered=!1;let deviceId=”;let currentView=’students’;const studentsContainer=document.getElementById(‘studentsContainer’);const groupsContainer=document.getElementById(‘groupsContainer’);const studentsView=document.getElementById(‘studentsView’);const groupsView=document.getElementById(‘groupsView’);const openAddModalBtn=document.getElementById(‘openAddModal’);const openSortModalBtn=document.getElementById(‘openSortModal’);const openRegisterModalBtn=document.getElementById(‘openRegisterModal’);const toggleViewBtn=document.getElementById(‘toggleView’);const clearGroupStarsBtn=document.getElementById(‘clearGroupStars’);const syncStarsBtn=document.getElementById(‘syncStars’);const addModal=document.getElementById(‘addModal’);const sortModal=document.getElementById(‘sortModal’);const registerModal=document.getElementById(‘registerModal’);const editGroupModal=document.getElementById(‘editGroupModal’);const closeAddModalBtn=document.getElementById(‘closeAddModal’);const closeImportModalBtn=document.getElementById(‘closeImportModal’);const closeSortModalBtn=document.getElementById(‘closeSortModal’);const closeRegisterModalBtn=document.getElementById(‘closeRegisterModal’);const addStudentBtn=document.getElementById(‘addStudent’);const importBtn=document.getElementById(‘importBtn’);const registerBtn=document.getElementById(‘registerBtn’);const studentNameInput=document.getElementById(‘studentName’);const importStudentsTextarea=document.getElementById(‘importStudents’);const editModal=document.getElementById(‘editModal’);const editStudentNameInput=document.getElementById(‘editStudentName’);const saveEditBtn=document.getElementById(‘saveEdit’);const deleteStudentBtn=document.getElementById(‘deleteStudent’);const cancelEditBtn=document.getElementById(‘cancelEdit’);const editGroupNameInput=document.getElementById(‘editGroupName’);const saveGroupEditBtn=document.getElementById(‘saveGroupEdit’);const deleteGroupBtn=document.getElementById(‘deleteGroup’);const cancelGroupEditBtn=document.getElementById(‘cancelGroupEdit’);const tabs=document.querySelectorAll(‘.tab’);const tabContents=document.querySelectorAll(‘.tab-content’);const sortTabs=document.querySelectorAll(‘.sort-tab’);const sortList=document.getElementById(‘sortList’);const deviceIdInput=document.getElementById(‘deviceId’);const registerCodeInput=document.getElementById(‘registerCode’);const unregisteredNotice=document.getElementById(‘unregisteredNotice’);const registerButton=document.getElementById(‘openRegisterModal’);const viewToggleBtns=document.querySelectorAll(‘.view-toggle-btn’);const addModalTitle=document.getElementById(‘addModalTitle’);function initApp(){generateDeviceId();checkRegistration();loadStudents();loadGroups();renderStudents();renderGroups();setupEventListeners()}
function generateDeviceId(){let storedDeviceId=localStorage.getItem(‘classStars_deviceId’);if(!storedDeviceId){const userAgent=navigator.userAgent;const language=navigator.language;const platform=navigator.platform;let hash=0;const str=userAgent+language+platform;for(let i=0;i<str.length;i++){const char=str.charCodeAt(i);hash=((hash<<5)-hash)+char;hash=hash&hash}
storedDeviceId=’DEV’+Math.abs(hash).toString(16).toUpperCase().substring(0,8);localStorage.setItem(‘classStars_deviceId’,storedDeviceId)}
deviceId=storedDeviceId;deviceIdInput.value=deviceId}
function checkRegistration(){const registrationData=localStorage.getItem(‘classStars_registration’);if(registrationData){const{deviceId:regDeviceId,code}=JSON.parse(registrationData);if(validateRegistrationCode(regDeviceId,code)){isRegistered=!0;registerButton.classList.add(‘registered’);registerButton.title=”软件已注册”;unregisteredNotice.style.display=’none’}else{isRegistered=!1;registerButton.classList.remove(‘registered’);registerButton.title=”注册软件”;unregisteredNotice.style.display=’block’}}else{isRegistered=!1;registerButton.classList.remove(‘registered’);registerButton.title=”注册软件”;unregisteredNotice.style.display=’block’}}
function validateRegistrationCode(deviceId,code){const expectedCode=generateRegistrationCode(deviceId);return code===expectedCode}
function generateRegistrationCode(deviceId){let hash=0;for(let i=0;i<deviceId.length;i++){const char=deviceId.charCodeAt(i);hash=((hash<<5)-hash)+char;hash=hash&hash}
const salt=”CLASSSTARS2024″;let fullHash=0;for(let i=0;i<salt.length;i++){const char=salt.charCodeAt(i);fullHash=((fullHash<<5)-fullHash)+char;fullHash=fullHash&fullHash}
fullHash=(fullHash+hash)&0xFFFFFFFF;return’REG’+Math.abs(fullHash).toString(16).toUpperCase().substring(0,8)}
function loadStudents(){const savedStudents=localStorage.getItem(‘classStars’);if(savedStudents){students=JSON.parse(savedStudents);students.forEach(student=>{if(student.sessionStars===undefined){student.sessionStars=0}})}}
function saveStudents(){localStorage.setItem(‘classStars’,JSON.stringify(students))}
function loadGroups(){const savedGroups=localStorage.getItem(‘classStars_groups’);if(savedGroups){groups=JSON.parse(savedGroups)}else{groups=[{id:’group1′,name:’第一组’,stars:0},{id:’group2′,name:’第二组’,stars:0},{id:’group3′,name:’第三组’,stars:0},{id:’group4′,name:’第四组’,stars:0},{id:’group5′,name:’第五组’,stars:0}];saveGroups()}}
function saveGroups(){localStorage.setItem(‘classStars_groups’,JSON.stringify(groups))}
function renderStudents(){if(students.length===0){studentsContainer.innerHTML=`<div class=”empty-state”><h3>还没有添加学生</h3><p>请点击右上角”+”按钮添加学生</p></div>`;return}
studentsContainer.innerHTML=”;students.forEach(student=>{const starCount=Math.min(student.sessionStars,10);const starsVisual=Array(starCount).fill(0).map(()=>'<i class=”fas fa-star star-icon-small”></i>’).join(”);const studentCard=document.createElement(‘div’);studentCard.className=’student-card’;studentCard.innerHTML=`
                    <div class=”card-actions”>
                        <button class=”edit-btn” data-id=”${student.id}” title=”编辑学生”><i class=”fas fa-edit”></i></button>
                    </div>
                    <div class=”student-name”>${student.name}</div>
                    <div class=”today-stars-visual”>${starsVisual}</div>
                    <div class=”stars-container”>
                        <div class=”today-stars”><div class=”stars-label”>本节星星</div><div class=”stars-count”>${student.sessionStars}</div></div>
                    </div>
                    <div class=”stars-controls”>
                        <button class=”add-today-star” data-id=”${student.id}”>+</button>
                        <button class=”remove-today-star” data-id=”${student.id}” ${!isRegistered ? ‘disabled’ : ”}>-</button>
                    </div>
                `;studentsContainer.appendChild(studentCard)})}
function renderGroups(){if(groups.length===0){groupsContainer.innerHTML=`<div class=”empty-state”><h3>还没有小组</h3><p>请点击右上角”+”按钮添加小组</p></div>`;return}
groupsContainer.innerHTML=”;groups.forEach(group=>{const starCount=Math.min(group.stars,10);const starsVisual=Array(starCount).fill(0).map(()=>'<i class=”fas fa-star star-icon-small”></i>’).join(”);const groupCard=document.createElement(‘div’);groupCard.className=’group-card’;groupCard.innerHTML=`
                    <div class=”group-card-actions”>
                        <button class=”group-edit-btn” data-id=”${group.id}” title=”编辑小组”><i class=”fas fa-edit”></i></button>
                    </div>
                    <div class=”group-name”>${group.name}</div>
                    <div class=”group-stars-visual”>${starsVisual}</div>
                    <div class=”group-stars-container”>
                        <div class=”group-stars”><div class=”stars-label”>小组星星</div><div class=”group-stars-count”>${group.stars}</div></div>
                    </div>
                    <div class=”group-stars-controls”>
                        <button class=”add-group-star” data-id=”${group.id}”>+</button>
                    </div>
                `;groupsContainer.appendChild(groupCard)})}
function renderSortList(){const sortedStudents=[…students].sort((a,b)=>{if(currentSortType===’session’){return b.sessionStars-a.sessionStars}else if(currentSortType===’today’){return b.todayStars-a.todayStars}else{return b.totalStars-a.totalStars}});sortList.innerHTML=”;if(sortedStudents.length===0){sortList.innerHTML=`<div class=”empty-state” style=”padding: 20px;”><p>还没有学生数据</p></div>`;return}
sortedStudents.forEach((student,index)=>{let starCount,starValue;if(currentSortType===’session’){starCount=Math.min(student.sessionStars,5);starValue=student.sessionStars}else if(currentSortType===’today’){starCount=Math.min(student.todayStars,5);starValue=student.todayStars}else{starCount=Math.min(student.totalStars,5);starValue=student.totalStars}
const starsVisual=Array(starCount).fill(0).map(()=>'<i class=”fas fa-star star-icon-small”></i>’).join(”);const listItem=document.createElement(‘div’);listItem.className=’sort-list-item’;listItem.innerHTML=`
                    <div class=”student-info”>
                        <div style=”font-weight: bold; width: 30px;”>${index + 1}</div>
                        <div class=”student-name”>${student.name}</div>
                    </div>
                    <div style=”display: flex; align-items: center; gap: 10px;”>
                        <div>${starsVisual}</div>
                        <div class=”stars-count”>${starValue}</div>
                    </div>
                `;sortList.appendChild(listItem)})}
function setupEventListeners(){openAddModalBtn.addEventListener(‘click’,()=>{if(currentView===’students’){addModalTitle.textContent=’添加学生’;addModal.style.display=’flex’;switchTab(‘single’)}else{addGroup()}});openSortModalBtn.addEventListener(‘click’,()=>{sortModal.style.display=’flex’;renderSortList()});openRegisterModalBtn.addEventListener(‘click’,()=>{registerModal.style.display=’flex’});toggleViewBtn.addEventListener(‘click’,toggleView);clearGroupStarsBtn.addEventListener(‘click’,clearGroupStars);syncStarsBtn.addEventListener(‘click’,syncSessionStars);viewToggleBtns.forEach(btn=>{btn.addEventListener(‘click’,()=>{const view=btn.getAttribute(‘data-view’);switchView(view)})});closeAddModalBtn.addEventListener(‘click’,closeAddModal);closeImportModalBtn.addEventListener(‘click’,closeAddModal);closeSortModalBtn.addEventListener(‘click’,closeSortModal);closeRegisterModalBtn.addEventListener(‘click’,closeRegisterModal);addStudentBtn.addEventListener(‘click’,addStudent);importBtn.addEventListener(‘click’,importStudents);registerBtn.addEventListener(‘click’,registerSoftware);studentsContainer.addEventListener(‘click’,handleStudentCardClick);groupsContainer.addEventListener(‘click’,handleGroupCardClick);saveEditBtn.addEventListener(‘click’,saveStudentEdit);deleteStudentBtn.addEventListener(‘click’,deleteStudent);cancelEditBtn.addEventListener(‘click’,closeEditModal);saveGroupEditBtn.addEventListener(‘click’,saveGroupEdit);deleteGroupBtn.addEventListener(‘click’,deleteGroup);cancelGroupEditBtn.addEventListener(‘click’,closeEditGroupModal);studentNameInput.addEventListener(‘keypress’,function(e){if(e.key===’Enter’){addStudent()}});tabs.forEach(tab=>{tab.addEventListener(‘click’,()=>{const tabId=tab.getAttribute(‘data-tab’);switchTab(tabId)})});sortTabs.forEach(tab=>{tab.addEventListener(‘click’,()=>{const sortType=tab.getAttribute(‘data-sort’);switchSortTab(sortType)})})}
function toggleView(){if(currentView===’students’){switchView(‘groups’)}else{switchView(‘students’)}}
function switchView(view){currentView=view;viewToggleBtns.forEach(btn=>{if(btn.getAttribute(‘data-view’)===view){btn.classList.add(‘active’)}else{btn.classList.remove(‘active’)}});if(view===’students’){studentsView.style.display=’block’;groupsView.style.display=’none’;toggleViewBtn.classList.remove(‘active’)}else{studentsView.style.display=’none’;groupsView.style.display=’block’;toggleViewBtn.classList.add(‘active’)}}
function switchTab(tabId){tabs.forEach(tab=>{if(tab.getAttribute(‘data-tab’)===tabId){tab.classList.add(‘active’)}else{tab.classList.remove(‘active’)}});tabContents.forEach(content=>{if(content.id===`${tabId}-tab`){content.classList.add(‘active’)}else{content.classList.remove(‘active’)}})}
function switchSortTab(sortType){currentSortType=sortType;sortTabs.forEach(tab=>{if(tab.getAttribute(‘data-sort’)===sortType){tab.classList.add(‘active’)}else{tab.classList.remove(‘active’)}});renderSortList()}
function closeAddModal(){addModal.style.display=’none’;studentNameInput.value=”;importStudentsTextarea.value=”}
function closeSortModal(){sortModal.style.display=’none’}
function closeRegisterModal(){registerModal.style.display=’none’;registerCodeInput.value=”}
function closeEditGroupModal(){editGroupModal.style.display=’none’;currentEditGroupId=null}
function registerSoftware(){const code=registerCodeInput.value.trim();if(!code){alert(‘请输入注册码’);return}
if(validateRegistrationCode(deviceId,code)){const registrationData={deviceId:deviceId,code:code};localStorage.setItem(‘classStars_registration’,JSON.stringify(registrationData));isRegistered=!0;registerButton.classList.add(‘registered’);registerButton.title=”软件已注册”;unregisteredNotice.style.display=’none’;alert(‘注册成功!累计星星功能已启用。’);closeRegisterModal();renderStudents()}else{alert(‘注册码无效,请检查后重试。’)}}
function addStudent(){const name=studentNameInput.value.trim();if(!name){alert(‘请输入学生姓名’);return}
if(students.some(student=>student.name===name)){alert(‘该学生姓名已存在,请使用其他姓名’);return}
const newStudent={id:Date.now().toString(),name:name,sessionStars:0,todayStars:0,totalStars:0};students.push(newStudent);saveStudents();renderStudents();closeAddModal()}
function addGroup(){const groupName=prompt(‘请输入小组名称:’);if(!groupName)return;const newGroup={id:’group’+Date.now().toString(),name:groupName,stars:0};groups.push(newGroup);saveGroups();renderGroups()}
function importStudents(){const namesText=importStudentsTextarea.value.trim();if(!namesText){alert(‘请输入学生姓名列表’);return}
const names=namesText.split(‘\n’).map(name=>name.trim()).filter(name=>name.length>0);if(names.length===0){alert(‘没有检测到有效的学生姓名’);return}
let addedCount=0;names.forEach(name=>{if(name&&!students.some(student=>student.name===name)){const newStudent={id:Date.now().toString()+Math.random().toString(36).substring(2,5),name:name,sessionStars:0,todayStars:0,totalStars:0};students.push(newStudent);addedCount++}});saveStudents();renderStudents();closeAddModal();alert(`成功添加 ${addedCount} 名学生`)}
function handleStudentCardClick(e){const target=e.target;const studentId=target.closest(‘button’)?.getAttribute(‘data-id’);if(!studentId)return;const student=students.find(s=>s.id===studentId);if(!student)return;if(target.closest(‘.edit-btn’)){openEditModal(student)}else if(target.closest(‘.add-today-star’)){student.sessionStars++;saveStudents();renderStudents()}else if(target.closest(‘.remove-today-star’)){if(student.sessionStars>0){student.sessionStars–;saveStudents();renderStudents()}}}
function handleGroupCardClick(e){const target=e.target;const groupId=target.closest(‘button’)?.getAttribute(‘data-id’);if(!groupId)return;const group=groups.find(g=>g.id===groupId);if(!group)return;if(target.closest(‘.group-edit-btn’)){openEditGroupModal(group)}else if(target.closest(‘.add-group-star’)){group.stars++;saveGroups();renderGroups()}}
function openEditModal(student){currentEditId=student.id;editStudentNameInput.value=student.name;editModal.style.display=’flex’}
function openEditGroupModal(group){currentEditGroupId=group.id;editGroupNameInput.value=group.name;editGroupModal.style.display=’flex’}
function closeEditModal(){editModal.style.display=’none’;currentEditId=null}
function saveStudentEdit(){const name=editStudentNameInput.value.trim();if(!name){alert(‘请输入学生姓名’);return}
const student=students.find(s=>s.id===currentEditId);if(student){student.name=name;saveStudents();renderStudents();closeEditModal()}}
function saveGroupEdit(){const name=editGroupNameInput.value.trim();if(!name){alert(‘请输入小组名称’);return}
const group=groups.find(g=>g.id===currentEditGroupId);if(group){group.name=name;saveGroups();renderGroups();closeEditGroupModal()}}
function deleteStudent(){if(confirm(‘确定要删除这名学生吗?此操作不可恢复。’)){students=students.filter(s=>s.id!==currentEditId);saveStudents();renderStudents();closeEditModal()}}
function deleteGroup(){if(confirm(‘确定要删除这个小组吗?此操作不可恢复。’)){groups=groups.filter(g=>g.id!==currentEditGroupId);saveGroups();renderGroups();closeEditGroupModal()}}
function clearGroupStars(){if(confirm(‘确定要清空所有小组的星星吗?此操作不可恢复。’)){groups.forEach(group=>{group.stars=0});saveGroups();renderGroups()}}
function syncSessionStars(){if(confirm(‘确定要同步并清空本节星星吗?本节星星将添加到今日星星和累计星星中,然后本节星星将被清空。’)){students.forEach(student=>{student.todayStars+=student.sessionStars;if(isRegistered){student.totalStars+=student.sessionStars}
student.sessionStars=0});saveStudents();renderStudents();alert(‘同步完成!本节星星已清空。’)}}
initApp()
    </script>
</body>
</html>

声明:来自浏览器与脚本,仅代表创作者观点。链接:https://eyangzhen.com/4031.html

浏览器与脚本的头像浏览器与脚本

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部