Files
ClassFeedback/.claude/skills/keping-simplified/assets/form_template.html
2026-06-02 23:01:58 +08:00

232 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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,'&quot;') + '">📋 ' + 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>