<!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>
核心功能与操作流程
您可以通过表单添加、修改学生成绩,并利用表格和搜索功能进行查看与管理。

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