如何做一个简单的学生成绩管理系统?

wen java案例 81
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">📚 简单学生成绩管理系统</title>
    <style>
        * {
            box-sizing: border-box;
            font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
        }
        body {
            background: #f4f7fc;
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 1.5rem;
        }
        .card {
            background: white;
            max-width: 1000px;
            width: 100%;
            border-radius: 28px;
            box-shadow: 0 20px 40px rgba(0,20,50,0.12);
            padding: 1.8rem 2rem 2.5rem;
            transition: 0.2s;
        }
        h1 {
            font-size: 1.8rem;
            font-weight: 600;
            margin-top: 0.2rem;
            margin-bottom: 0.3rem;
            color: #1e2a3a;
            display: flex;
            align-items: center;
            gap: 0.5rem;
            border-bottom: 3px solid #e9edf4;
            padding-bottom: 0.75rem;
        }
        h1 small {
            font-size: 0.9rem;
            font-weight: 400;
            color: #5e6f8d;
            margin-left: auto;
        }
        .grid-2col {
            display: grid;
            grid-template-columns: 1.2fr 2fr;
            gap: 1.8rem;
            margin-top: 1.5rem;
        }
        /* 表单区域 */
        .form-section {
            background: #f9faff;
            border-radius: 20px;
            padding: 1.5rem 1.2rem 1.8rem;
            box-shadow: inset 0 0 0 1px rgba(0,0,0,0.02);
        }
        .form-section h2 {
            font-size: 1.3rem;
            margin-top: 0;
            margin-bottom: 1rem;
            font-weight: 600;
            color: #203349;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .input-group {
            margin-bottom: 1.1rem;
        }
        .input-group label {
            display: block;
            font-size: 0.85rem;
            font-weight: 500;
            color: #2c3e5a;
            margin-bottom: 0.25rem;
        }
        .input-group input {
            width: 100%;
            padding: 0.7rem 0.9rem;
            font-size: 0.95rem;
            border: 1px solid #d7dee9;
            border-radius: 14px;
            background: white;
            transition: 0.15s;
            outline: none;
        }
        .input-group input:focus {
            border-color: #3b7cff;
            box-shadow: 0 0 0 3px rgba(59, 124, 255, 0.15);
        }
        .input-row {
            display: flex;
            gap: 0.8rem;
            flex-wrap: wrap;
        }
        .input-row .input-group {
            flex: 1;
            min-width: 70px;
        }
        .btn {
            background: white;
            border: 1px solid #d0d8e3;
            border-radius: 40px;
            padding: 0.6rem 1.2rem;
            font-weight: 500;
            font-size: 0.9rem;
            cursor: pointer;
            transition: 0.15s;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
            color: #1f2b3c;
        }
        .btn-primary {
            background: #1e3c72;
            border: 1px solid #1e3c72;
            color: white;
            box-shadow: 0 4px 8px rgba(30, 60, 114, 0.12);
        }
        .btn-primary:hover {
            background: #15305c;
            transform: scale(0.98);
        }
        .btn-outline {
            background: transparent;
            border: 1px solid #ccd7e6;
        }
        .btn-outline:hover {
            background: #eef3fa;
        }
        .btn-danger {
            background: #ffe7e7;
            border: 1px solid #ffbaba;
            color: #a12b2b;
        }
        .btn-danger:hover {
            background: #ffd4d4;
        }
        .btn-sm {
            padding: 0.35rem 0.9rem;
            font-size: 0.8rem;
        }
        .action-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 0.6rem;
            margin-top: 1.2rem;
            justify-content: flex-start;
        }
        .action-buttons .btn {
            flex: 1 0 auto;
        }
        .info-note {
            font-size: 0.75rem;
            color: #5f7492;
            margin-top: 0.8rem;
            background: #eef3fa;
            padding: 0.5rem 0.8rem;
            border-radius: 40px;
            display: inline-block;
        }
        /* 表格区域 */
        .table-section {
            min-width: 0;
        }
        .toolbar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 8px 12px;
            margin-bottom: 0.8rem;
        }
        .toolbar h2 {
            font-size: 1.3rem;
            font-weight: 600;
            margin: 0;
            color: #203349;
        }
        .search-box {
            display: flex;
            align-items: center;
            gap: 6px;
            background: #f2f6fd;
            border-radius: 40px;
            padding: 0.2rem 0.2rem 0.2rem 1rem;
            border: 1px solid #dee6f0;
        }
        .search-box input {
            border: none;
            background: transparent;
            padding: 0.4rem 0;
            font-size: 0.9rem;
            outline: none;
            width: 130px;
        }
        .search-box input::placeholder {
            color: #8a9bb5;
        }
        .search-box .btn {
            border-radius: 40px;
            padding: 0.4rem 0.9rem;
            background: #1e3c72;
            color: white;
            border: none;
            font-size: 0.8rem;
        }
        .table-wrapper {
            overflow-x: auto;
            border-radius: 20px;
            background: #fbfdff;
            border: 1px solid #e4ebf5;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            font-size: 0.9rem;
            min-width: 400px;
        }
        th {
            background: #eaf0f9;
            color: #17345c;
            font-weight: 600;
            padding: 0.8rem 0.6rem;
            text-align: left;
            border-bottom: 2px solid #d2dcee;
        }
        td {
            padding: 0.7rem 0.6rem;
            border-bottom: 1px solid #eaeef6;
            color: #1e2d44;
        }
        tr:last-child td {
            border-bottom: none;
        }
        .badge {
            background: #d7e2f5;
            color: #1f3f6d;
            border-radius: 40px;
            padding: 0.15rem 0.6rem;
            font-size: 0.7rem;
            font-weight: 500;
            margin-left: 6px;
        }
        .empty-row td {
            text-align: center;
            padding: 3rem 1rem;
            color: #7487a6;
            font-style: italic;
        }
        .text-muted {
            color: #6e7f98;
        }
        .grade-high {
            font-weight: 600;
            color: #1b6b3a;
        }
        .grade-low {
            font-weight: 500;
            color: #b34a2c;
        }
        .footer-actions {
            margin-top: 15px;
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
        }
        @media (max-width: 700px) {
            .card {
                padding: 1.2rem;
            }
            .grid-2col {
                grid-template-columns: 1fr;
                gap: 1rem;
            }
            .search-box input {
                width: 100px;
            }
        }
    </style>
</head>
<body>
<div class="card">
    <h1>
        🎓 成绩管理
        <small>简单 · 本地存储</small>
    </h1>
    <div class="grid-2col">
        <!-- 左侧:添加 / 编辑 -->
        <div class="form-section">
            <h2>➕ 添加 / 编辑</h2>
            <div class="input-group">
                <label>👤 学生姓名</label>
                <input type="text" id="studentName" placeholder=" 张三" maxlength="20" value="">
            </div>
            <div class="input-row">
                <div class="input-group">
                    <label>📘 语文</label>
                    <input type="number" id="scoreChinese" min="0" max="100" placeholder="0-100" value="">
                </div>
                <div class="input-group">
                    <label>📗 数学</label>
                    <input type="number" id="scoreMath" min="0" max="100" placeholder="0-100" value="">
                </div>
                <div class="input-group">
                    <label>📙 英语</label>
                    <input type="number" id="scoreEnglish" min="0" max="100" placeholder="0-100" value="">
                </div>
            </div>
            <div class="action-buttons">
                <button class="btn btn-primary" id="addBtn">➕ 增加学生</button>
                <button class="btn btn-outline" id="updateBtn">✏️ 更新选中</button>
                <button class="btn btn-outline" id="clearFormBtn">🗑️ 清空</button>
            </div>
            <div class="info-note">
                ⚡ 点击表格行 · 自动填入表单修改
            </div>
        </div>
        <!-- 右侧:列表 & 搜索 -->
        <div class="table-section">
            <div class="toolbar">
                <h2>📋 成绩单</h2>
                <div class="search-box">
                    <input type="text" id="searchInput" placeholder="搜索姓名...">
                    <button class="btn" id="searchBtn">🔍</button>
                </div>
            </div>
            <div class="table-wrapper">
                <table id="scoreTable">
                    <thead>
                        <tr>
                            <th>姓名</th>
                            <th>语文</th>
                            <th>数学</th>
                            <th>英语</th>
                            <th>均分</th>
                            <th style="width: 50px;">操作</th>
                        </tr>
                    </thead>
                    <tbody id="tableBody">
                        <!-- 动态渲染 -->
                    </tbody>
                </table>
            </div>
            <div class="footer-actions">
                <button class="btn btn-outline btn-sm" id="sortByNameBtn">📌 按姓名排序</button>
                <button class="btn btn-outline btn-sm" id="sortByAvgBtn">📊 按均分排序</button>
                <span style="flex:1;"></span>
                <button class="btn btn-danger btn-sm" id="deleteAllBtn">🗑️ 清空全部</button>
            </div>
            <div style="margin-top: 8px; font-size:0.8rem; color:#3a577a;">
                👆 点击行选中 · 双击直接编辑
            </div>
        </div>
    </div>
</div>
<script>
    (function() {
        // ---------- 存储 ----------
        const STORAGE_KEY = 'simple_grade_system';
        // 数据格式:[{ id, name, chinese, math, english }]
        let students = [];
        // DOM 元素
        const nameInput = document.getElementById('studentName');
        const chineseInput = document.getElementById('scoreChinese');
        const mathInput = document.getElementById('scoreMath');
        const englishInput = document.getElementById('scoreEnglish');
        const addBtn = document.getElementById('addBtn');
        const updateBtn = document.getElementById('updateBtn');
        const clearFormBtn = document.getElementById('clearFormBtn');
        const tableBody = document.getElementById('tableBody');
        const searchInput = document.getElementById('searchInput');
        const searchBtn = document.getElementById('searchBtn');
        const sortByNameBtn = document.getElementById('sortByNameBtn');
        const sortByAvgBtn = document.getElementById('sortByAvgBtn');
        const deleteAllBtn = document.getElementById('deleteAllBtn');
        // 当前选中的行id (编辑时使用)
        let selectedStudentId = null;
        // ---------- 辅助函数 ----------
        function generateId() {
            return Date.now().toString(36) + Math.random().toString(36).substring(2, 6);
        }
        // 从 localStorage 读取
        function loadFromStorage() {
            const stored = localStorage.getItem(STORAGE_KEY);
            if (stored) {
                try {
                    students = JSON.parse(stored);
                    // 保证每个对象都有 id (旧数据兼容)
                    students = students.filter(s => s && typeof s === 'object');
                    students.forEach(s => {
                        if (!s.id) s.id = generateId();
                    });
                } catch (e) {
                    students = [];
                }
            } else {
                // 默认两条示例数据
                students = [
                    { id: generateId(), name: '王小明', chinese: 88, math: 92, english: 79 },
                    { id: generateId(), name: '李华', chinese: 73, math: 64, english: 81 },
                ];
            }
        }
        // 保存到 localStorage
        function saveToStorage() {
            localStorage.setItem(STORAGE_KEY, JSON.stringify(students));
        }
        // 计算平均分 (保留1位)
        function calcAverage(s) {
            return ((s.chinese + s.math + s.english) / 3).toFixed(1);
        }
        // 获取搜索关键词 (小写)
        function getSearchKeyword() {
            return searchInput.value.trim().toLowerCase();
        }
        // 过滤 & 克隆学生列表 (不改变原数组)
        function getFilteredList() {
            const keyword = getSearchKeyword();
            if (!keyword) return students.slice();
            return students.filter(s => s.name.toLowerCase().includes(keyword));
        }
        // ---------- 渲染表格 ----------
        function renderTable() {
            const filtered = getFilteredList();
            // 排序 (如果之后点排序会重新排序源数组? 我们保留源顺序,但排序按钮会重新排序 students 并保存)
            // 但为了显示搜索,这里直接用过滤后的数据,但排序状态已经在 students 上。
            // 所以我们采用:如果搜索,展示过滤后的顺序;如果没搜索,就用 students 当前顺序。
            // 但 sort 按钮改变的是 students 数组本身,很好。
            const list = filtered.length ? filtered : students; 
            // 但如果有搜索关键词,filtered 可能为空,显示无数据就好。
            const dataToRender = (getSearchKeyword() === '') ? students : filtered;
            if (!dataToRender.length) {
                tableBody.innerHTML = `
                    <tr class="empty-row">
                        <td colspan="6">📭 没有学生记录,添加一位吧</td>
                    </tr>
                `;
                return;
            }
            let html = '';
            dataToRender.forEach((s, index) => {
                const avg = calcAverage(s);
                const isSelected = (selectedStudentId === s.id);
                const rowClass = isSelected ? 'selected-row' : '';
                // 标记颜色 (均分高低)
                const avgNum = parseFloat(avg);
                const gradeClass = avgNum >= 80 ? 'grade-high' : (avgNum < 60 ? 'grade-low' : '');
                html += `
                    <tr data-id="${s.id}" class="${rowClass}" style="cursor:pointer; ${isSelected ? 'background:#e9f0fe;' : ''}">
                        <td><strong>${s.name}</strong></td>
                        <td>${s.chinese}</td>
                        <td>${s.math}</td>
                        <td>${s.english}</td>
                        <td class="${gradeClass}">${avg}</td>
                        <td>
                            <button class="btn btn-danger btn-sm delete-single" data-id="${s.id}" style="padding:0.2rem 0.7rem;">✕</button>
                        </td>
                    </tr>
                `;
            });
            tableBody.innerHTML = html;
            // 绑定行点击事件 (选中)
            document.querySelectorAll('#tableBody tr[data-id]').forEach(row => {
                row.addEventListener('click', function(e) {
                    // 如果点击的是删除按钮,不触发选中
                    if (e.target.closest('.delete-single')) return;
                    const id = this.dataset.id;
                    selectStudentById(id);
                });
                // 双击直接填充表单 (更方便)
                row.addEventListener('dblclick', function(e) {
                    if (e.target.closest('.delete-single')) return;
                    const id = this.dataset.id;
                    selectStudentById(id);
                    // 自动聚焦姓名
                    nameInput.focus();
                });
            });
            // 绑定单个删除按钮
            document.querySelectorAll('.delete-single').forEach(btn => {
                btn.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const id = this.dataset.id;
                    if (!id) return;
                    if (confirm('确定删除该学生成绩吗?')) {
                        students = students.filter(s => s.id !== id);
                        if (selectedStudentId === id) {
                            clearForm();
                            selectedStudentId = null;
                        }
                        saveToStorage();
                        renderTable();
                    }
                });
            });
        }
        // ---------- 选中学生 (填充表单) ----------
        function selectStudentById(id) {
            const student = students.find(s => s.id === id);
            if (!student) return;
            selectedStudentId = student.id;
            nameInput.value = student.name;
            chineseInput.value = student.chinese;
            mathInput.value = student.math;
            englishInput.value = student.english;
            renderTable();  // 重新渲染高亮
            // 更新按钮文案
            addBtn.textContent = '➕ 增加学生';
        }
        // 清空表单 & 取消选中
        function clearForm() {
            nameInput.value = '';
            chineseInput.value = '';
            mathInput.value = '';
            englishInput.value = '';
            selectedStudentId = null;
            addBtn.textContent = '➕ 增加学生';
            renderTable(); // 取消高亮
        }
        // ---------- 表单验证 ----------
        function getFormData() {
            const name = nameInput.value.trim();
            if (!name) {
                alert('请输入学生姓名');
                return null;
            }
            const chinese = parseFloat(chineseInput.value);
            const math = parseFloat(mathInput.value);
            const english = parseFloat(englishInput.value);
            if (isNaN(chinese) || isNaN(math) || isNaN(english)) {
                alert('请完整填写三门成绩 (0-100)');
                return null;
            }
            if (chinese < 0 || chinese > 100 || math < 0 || math > 100 || english < 0 || english > 100) {
                alert('成绩范围:0 ~ 100');
                return null;
            }
            return { name, chinese, math, english };
        }
        // ---------- 增加学生 ----------
        function addStudent() {
            const data = getFormData();
            if (!data) return;
            // 如果当前有选中?增加时如果选中状态,默认取消 (或者可以当作增加新学生)
            // 但更符合直觉:如果有选中,先清除选中再添加?我们采取:直接新增,不管选中,但清空表单。
            const newStudent = {
                id: generateId(),
                name: data.name,
                chinese: data.chinese,
                math: data.math,
                english: data.english,
            };
            students.push(newStudent);
            saveToStorage();
            clearForm();  // 清空并取消选中
            renderTable();
            // 滚动到底部查看最新
            const tableWrapper = document.querySelector('.table-wrapper');
            if (tableWrapper) tableWrapper.scrollTop = tableWrapper.scrollHeight;
        }
        // ---------- 更新学生 (选中) ----------
        function updateStudent() {
            if (!selectedStudentId) {
                alert('请先在表格中点击选择要修改的学生');
                return;
            }
            const data = getFormData();
            if (!data) return;
            const index = students.findIndex(s => s.id === selectedStudentId);
            if (index === -1) {
                alert('数据异常,请刷新');
                return;
            }
            // 检查姓名是否与其他学生冲突?不要紧了,可以重名。
            students[index].name = data.name;
            students[index].chinese = data.chinese;
            students[index].math = data.math;
            students[index].english = data.english;
            saveToStorage();
            // 不清除选中,但刷新高亮
            renderTable();
            // 保持表单填充的是当前数据
            // 但可以保持
            addBtn.textContent = '➕ 增加学生';
        }
        // ---------- 排序 ----------
        function sortByName() {
            students.sort((a, b) => a.name.localeCompare(b.name, 'zh'));
            saveToStorage();
            renderTable();
        }
        function sortByAvg() {
            students.sort((a, b) => {
                const avgA = (a.chinese + a.math + a.english) / 3;
                const avgB = (b.chinese + b.math + b.english) / 3;
                return avgB - avgA; // 从高到低
            });
            saveToStorage();
            renderTable();
        }
        // 清空全部
        function deleteAll() {
            if (!students.length) return;
            if (confirm('⚠️ 确定删除所有学生成绩?不可恢复!')) {
                students = [];
                selectedStudentId = null;
                clearForm();
                saveToStorage();
                renderTable();
            }
        }
        // 搜索
        function performSearch() {
            renderTable();
        }
        // ---------- 初始化事件绑定 ----------
        function init() {
            loadFromStorage();
            renderTable();
            // 如果没有任何学生,填充示例
            if (!students.length) {
                students = [
                    { id: generateId(), name: '赵小雅', chinese: 91, math: 87, english: 94 },
                    { id: generateId(), name: '陈瑞', chinese: 62, math: 58, english: 71 },
                ];
                saveToStorage();
                renderTable();
            }
            // 按钮事件
            addBtn.addEventListener('click', addStudent);
            updateBtn.addEventListener('click', updateStudent);
            clearFormBtn.addEventListener('click', clearForm);
            searchBtn.addEventListener('click', performSearch);
            searchInput.addEventListener('keyup', function(e) {
                if (e.key === 'Enter') performSearch();
            });
            sortByNameBtn.addEventListener('click', sortByName);
            sortByAvgBtn.addEventListener('click', sortByAvg);
            deleteAllBtn.addEventListener('click', deleteAll);
            // 当点击表格空白处取消选中?可以通过点击其他地方,但为了体验,增加一个全局点击? 
            // 但为了避免干扰,可以点击清空表单按钮来取消选中。
            // 另:点击行外区域不清除,用户可主动点清空。
        }
        init();
    })();
</script>
</body>
</html>

核心功能与操作流程

您可以通过表单添加、修改学生成绩,并利用表格和搜索功能进行查看与管理。

如何做一个简单的学生成绩管理系统?

  • 数据管理:左侧表单用于添加新学生或修改已有学生的姓名及三门课程(语文、数学、英语)的成绩,点击表格中的任意一行,其数据会自动填充到表单中,方便您进行修改。
  • 信息浏览:右侧表格会清晰展示所有学生的成绩列表,包括各科分数和平均分,您可以使用搜索框按姓名快速查找学生,或点击“按姓名排序”、“按均分排序”按钮来整理数据。
  • 数据维护:每个学生记录旁都有删除按钮,方便您移除单个学生,页面底部的“清空全部”按钮可用于重置所有数据,所有操作都会自动保存到浏览器的本地存储中,刷新页面后数据不会丢失。

抱歉,评论功能暂时关闭!