232 lines
11 KiB
HTML
232 lines
11 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>课评填写表单 - 穹狼乐高编程</title>
|
||
<style>
|
||
:root {
|
||
--primary:#6366f1; --secondary:#8b5cf6; --accent:#f59e0b; --success:#10b981;
|
||
--bg:#f8fafc; --card:#ffffff; --text:#1e293b;
|
||
}
|
||
body { font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif; background:var(--bg); color:var(--text); margin:0; padding:20px; }
|
||
.container { max-width:900px; margin:0 auto; }
|
||
.card { background:var(--card); border-radius:16px; padding:24px; box-shadow:0 4px 20px rgba(0,0,0,0.08); margin-bottom:16px; }
|
||
.header { text-align:center; padding:16px 0; }
|
||
.header h1 { font-size:24px; margin:0 0 8px 0; }
|
||
.header .meta { color:#64748b; font-size:14px; }
|
||
.progress { margin:16px 0; }
|
||
.progress-bar { display:flex; gap:8px; align-items:center; }
|
||
.progress-text { font-weight:600; min-width:80px; }
|
||
.progress-track { flex:1; height:12px; background:#e2e8f0; border-radius:999px; overflow:hidden; }
|
||
.progress-fill { height:100%; background:linear-gradient(90deg,var(--primary),var(--secondary)); border-radius:999px; transition:width 0.3s ease; }
|
||
.student { display:flex; gap:12px; align-items:start; padding:16px; background:#fafafa; border-radius:12px; margin-bottom:12px; }
|
||
.student-header { min-width:140px; }
|
||
.student-name { font-weight:700; font-size:18px; margin-bottom:8px; }
|
||
.student-profile { font-size:12px; color:#94a3b8; margin-bottom:6px; max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||
.status-buttons { display:flex; gap:6px; flex-wrap:wrap; }
|
||
.status-btn { padding:6px 12px; border-radius:999px; border:2px solid #e2e8f0; background:#fff; cursor:pointer; font-weight:600; font-size:13px; transition:all 0.2s; }
|
||
.status-btn:hover { border-color:#cbd5e1; }
|
||
.status-btn.active { background:#ecfdf5; border-color:#10b981; color:#059669; }
|
||
.status-btn[data-status=请假].active { background:#fef2f2; border-color:#f87171; color:#dc2626; }
|
||
.status-btn[data-status=补课].active { background:#fffbeb; border-color:#f59e0b; color:#d97706; }
|
||
.status-btn[data-status=体验].active { background:#eff6ff; border-color:#3b82f6; color:#2563eb; }
|
||
.input-group { flex:1; display:flex; flex-direction:column; gap:8px; }
|
||
.input-group label { font-size:13px; font-weight:600; color:#475569; }
|
||
.input-group textarea { width:100%; padding:12px; border:2px solid #e2e8f0; border-radius:10px; min-height:80px; resize:vertical; font-size:14px; transition:border-color 0.2s; }
|
||
.input-group textarea:focus { outline:none; border-color:var(--primary); }
|
||
.symbols { font-size:12px; color:#94a3b8; display:flex; gap:12px; flex-wrap:wrap; }
|
||
.add-student { border:2px dashed #cbd5e1; padding:16px; border-radius:12px; text-align:center; cursor:pointer; color:#64748b; transition:all 0.2s; }
|
||
.add-student:hover { border-color:var(--primary); color:var(--primary); background:#eef2ff; }
|
||
.actions { display:flex; gap:12px; justify-content:center; padding-top:16px; flex-wrap:wrap; }
|
||
.btn { padding:14px 32px; border-radius:10px; font-weight:700; font-size:16px; cursor:pointer; border:none; transition:transform 0.1s,opacity 0.2s; }
|
||
.btn-primary { background:linear-gradient(135deg,var(--primary),var(--secondary)); color:#fff; }
|
||
.btn-success { background:linear-gradient(135deg,var(--success),#059669); color:#fff; }
|
||
.btn:hover { transform:translateY(-2px); }
|
||
.btn:active { transform:translateY(0); }
|
||
.btn:disabled { opacity:0.5; cursor:not-allowed; transform:none; }
|
||
.course-card { background:linear-gradient(135deg,#6366f115,#8b5cf615); border-left:4px solid var(--primary); }
|
||
.course-title { font-size:18px; font-weight:700; margin:0 0 8px 0; }
|
||
.course-details { color:#475569; line-height:1.6; }
|
||
.toast { position:fixed; top:20px; left:50%; transform:translateX(-50%); padding:16px 32px; border-radius:12px; color:#fff; font-weight:600; z-index:1000; display:none; animation:fadeIn 0.3s; }
|
||
.toast.success { background:var(--success); }
|
||
.toast.error { background:#ef4444; }
|
||
.toast.info { background:var(--primary); }
|
||
@keyframes fadeIn { from{opacity:0;transform:translateX(-50%) translateY(-10px);} to{opacity:1;transform:translateX(-50%) translateY(0);} }
|
||
.spinner { display:inline-block; width:16px; height:16px; border:2px solid #fff; border-top-color:transparent; border-radius:50%; animation:spin 0.6s linear infinite; margin-right:8px; vertical-align:middle; }
|
||
@keyframes spin { to{transform:rotate(360deg);} }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="card header">
|
||
<h1>📝 课评填写表单</h1>
|
||
<div class="meta" id="classInfo">班级:发现世界-周四19点 | 第11周 | 2026-05-16</div>
|
||
<div class="progress">
|
||
<div class="progress-bar">
|
||
<div class="progress-text" id="progressText">0/5 完成</div>
|
||
<div class="progress-track"><div class="progress-fill" id="progressFill" style="width:0%"></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card course-card">
|
||
<div class="course-title" id="courseTitle">📚 课程目标:DISC-011 蜥蜴</div>
|
||
<div class="course-details" id="courseDetails">
|
||
核心知识点:关节连接、可动结构、仿生设计<br>
|
||
课时长度:60分钟
|
||
</div>
|
||
</div>
|
||
<div id="studentsList"></div>
|
||
<div class="add-student" id="addStudentBtn">+ 添加临时学生(补课/体验)</div>
|
||
<div class="actions">
|
||
<button class="btn btn-primary" id="submitBtn">🚀 提交并保存</button>
|
||
</div>
|
||
</div>
|
||
<div class="toast" id="toast"></div>
|
||
<script>
|
||
const SERVER_URL = window.location.origin;
|
||
|
||
const templateStudents = [{name:"梁家铭", status:"出勤", performance:""},{name:"简思瑜", status:"出勤", performance:""},{name:"简思霖", status:"请假", performance:""},{name:"罗钧龄", status:"出勤", performance:""},{name:"黄晓瑜", status:"出勤", performance:""},{name:"黄馨宸", status:"请假", performance:""}];
|
||
|
||
let students = [...templateStudents];
|
||
let currentClass = "发现世界-周四19点";
|
||
let currentWeek = 11;
|
||
let currentDate = "2026-05-16";
|
||
let courseInfo = {code:"DISC-011", title:"蜥蜴", knowledge:["关节连接","可动结构","仿生设计"], duration:"60分钟"};
|
||
|
||
function showToast(msg, type) {
|
||
const t = document.getElementById("toast");
|
||
t.textContent = msg;
|
||
t.className = "toast " + type;
|
||
t.style.display = "block";
|
||
setTimeout(function(){ t.style.display = "none"; }, 3000);
|
||
}
|
||
|
||
function init() {
|
||
renderStudents();
|
||
updateProgress();
|
||
}
|
||
|
||
function renderStudents() {
|
||
var container = document.getElementById("studentsList");
|
||
container.innerHTML = "";
|
||
students.forEach(function(s, idx) {
|
||
var el = document.createElement("div");
|
||
el.className = "student";
|
||
var profileHtml = s.profile ? '<div class="student-profile" title="' + s.profile.replace(/"/g,'"') + '">📋 ' + s.profile.substring(0,40) + '...</div>' : '';
|
||
el.innerHTML =
|
||
'<div class="student-header">' +
|
||
'<div class="student-name">' + s.name + '</div>' +
|
||
profileHtml +
|
||
'<div class="status-buttons">' +
|
||
'<button class="status-btn' + (s.status=='出勤'?' active':'') + '" data-status="出勤" data-idx="' + idx + '">出勤</button>' +
|
||
'<button class="status-btn' + (s.status=='请假'?' active':'') + '" data-status="请假" data-idx="' + idx + '">请假</button>' +
|
||
'<button class="status-btn' + (s.status=='补课'?' active':'') + '" data-status="补课" data-idx="' + idx + '">补课</button>' +
|
||
'<button class="status-btn' + (s.status=='体验'?' active':'') + '" data-status="体验" data-idx="' + idx + '">体验</button>' +
|
||
'</div></div>' +
|
||
'<div class="input-group">' +
|
||
'<label>课堂表现(支持速记:gj观望、zd主动)</label>' +
|
||
'<textarea id="perf-' + idx + '" placeholder="例如:gj5观望5分钟→zd3主动搭3层→zt自己调整,开心↑">' + (s.performance||'') + '</textarea>' +
|
||
'<div class="symbols">' +
|
||
'<span>💡 gj#=观望#分钟</span><span>💡 zd#=主动搭#层</span><span>💡 zt=自己调整</span>' +
|
||
'<span>💡 ↑=开心</span><span>💡 →=稳定</span><span>💡 ++=进步</span>' +
|
||
'</div></div>';
|
||
container.appendChild(el);
|
||
});
|
||
|
||
document.querySelectorAll(".status-btn").forEach(function(b) {
|
||
b.addEventListener("click", function(e) {
|
||
var idx = parseInt(b.dataset.idx);
|
||
students[idx].status = b.dataset.status;
|
||
renderStudents();
|
||
updateProgress();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll(".input-group textarea").forEach(function(t) {
|
||
t.addEventListener("input", function(e) {
|
||
var idx = parseInt(t.id.replace("perf-",""));
|
||
students[idx].performance = t.value;
|
||
updateProgress();
|
||
});
|
||
});
|
||
}
|
||
|
||
function updateProgress() {
|
||
var done = students.filter(function(x) {
|
||
return x.status !== '出勤' || (x.performance && x.performance.trim());
|
||
}).length;
|
||
var total = students.length;
|
||
var pct = Math.round((done/total)*100);
|
||
document.getElementById("progressText").textContent = done + '/' + total + ' 完成';
|
||
document.getElementById("progressFill").style.width = pct + '%';
|
||
}
|
||
|
||
function addStudent() {
|
||
students.push({name:"新学生", status:"体验", performance:"", profile:""});
|
||
renderStudents();
|
||
updateProgress();
|
||
}
|
||
|
||
function submitForm() {
|
||
var btn = document.getElementById("submitBtn");
|
||
var origText = btn.textContent;
|
||
|
||
// 检查出勤学生是否都填了表现
|
||
var unfilled = students.filter(function(s) {
|
||
return s.status === '出勤' && (!s.performance || !s.performance.trim());
|
||
});
|
||
if (unfilled.length > 0) {
|
||
var names = unfilled.map(function(s){ return s.name; }).join('、');
|
||
showToast('⚠️ 出勤学生 ' + names + ' 还未填写表现', 'error');
|
||
return;
|
||
}
|
||
|
||
// 按钮loading状态
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<span class="spinner"></span>提交中...';
|
||
|
||
var payload = {
|
||
class: currentClass,
|
||
week: currentWeek,
|
||
date: currentDate,
|
||
course: courseInfo,
|
||
students: students.map(function(s) {
|
||
return {
|
||
name: s.name,
|
||
status: s.status,
|
||
performance: s.performance || ''
|
||
};
|
||
})
|
||
};
|
||
|
||
fetch(SERVER_URL + '/api/submit', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify(payload)
|
||
})
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
if (data.success) {
|
||
showToast('✅ 提交成功!' + data.message, 'success');
|
||
btn.innerHTML = '✅ 已保存 — 可关闭此页面';
|
||
btn.className = 'btn btn-success';
|
||
} else {
|
||
showToast('❌ 提交失败:' + data.message, 'error');
|
||
btn.disabled = false;
|
||
btn.textContent = origText;
|
||
}
|
||
})
|
||
.catch(function(err) {
|
||
showToast('❌ 网络错误:无法连接服务器,请确认服务器已启动', 'error');
|
||
btn.disabled = false;
|
||
btn.textContent = origText;
|
||
});
|
||
}
|
||
|
||
document.getElementById("addStudentBtn").addEventListener("click", addStudent);
|
||
document.getElementById("submitBtn").addEventListener("click", submitForm);
|
||
init();
|
||
</script>
|
||
</body>
|
||
</html> |