fix: require manual summary fields before registration

This commit is contained in:
Codex
2026-06-15 16:35:46 +08:00
parent 854e4942ba
commit a7fbe91266
4 changed files with 174 additions and 79 deletions
+1 -1
View File
@@ -319,6 +319,6 @@
</section>
</main>
<script src="/static/admin.js?v=20260615-summary-register-delete"></script>
<script src="/static/admin.js?v=20260615-summary-register-complete"></script>
</body>
</html>
+67 -6
View File
@@ -75,6 +75,14 @@ let currentSummaryReviews = [];
let activeSummaryReview = null;
let summaryRegisterItemSeq = 0;
const SUMMARY_REQUIRED_FIELDS = [
["student", "学生"],
["date", "日期"],
["time", "时间"],
["teacher", "老师"],
["subject", "科目"],
];
function fmtHours(value) {
return Number(value || 0).toLocaleString("zh-CN", { maximumFractionDigits: 2 });
}
@@ -596,20 +604,73 @@ function addSummaryRegisterItem(value = "") {
const item = document.createElement("div");
item.className = "summary-register-item";
item.innerHTML = `<label for="${itemId}">课程小结</label>
<textarea id="${itemId}" rows="7" placeholder="每个输入框填写一条课程小结">${escapeHtml(value)}</textarea>
<textarea id="${itemId}" data-summary-field="body" rows="7" placeholder="每个输入框填写一条课程小结">${escapeHtml(value)}</textarea>
<div class="summary-register-fields">
<input data-summary-field="student" autocomplete="off" placeholder="学生" />
<input data-summary-field="date" autocomplete="off" placeholder="日期 2026.06.15" />
<input data-summary-field="time" autocomplete="off" placeholder="时间 08:00-09:00" />
<input data-summary-field="teacher" autocomplete="off" placeholder="老师" />
<input data-summary-field="subject" autocomplete="off" placeholder="科目" />
</div>
<p class="summary-register-missing" hidden></p>
<button class="small-button summary-register-remove" type="button">删除本框</button>`;
summaryRegisterItems.appendChild(item);
}
function inferSummaryField(body, field) {
const patterns = {
student: /(?:学生|学员)[:]\s*([^\n;,]+)/,
date: /(?:日期|上课日期)[:]\s*(\d{4}[./-]\d{1,2}[./-]\d{1,2})|(\d{4}[./-]\d{1,2}[./-]\d{1,2})/,
time: /(?:时间|上课时间)[:]\s*(\d{1,2}:\d{2}-\d{1,2}:\d{2})|(\d{1,2}:\d{2}-\d{1,2}:\d{2})/,
teacher: /(?:老师|教师)[:]\s*([^\n;,]+)|([\u4e00-\u9fa5A-Za-z0-9]{1,8}老师)/,
subject: /(?:科目|课程)[:]\s*([^\n;,]+)|(数学|语文|英语|物理|化学|生物|历史|地理|政治|道法)/,
};
const match = String(body || "").match(patterns[field]);
if (!match) return "";
return (match[1] || match[2] || "").trim();
}
function summaryRegisterItemPayload(item) {
const body = item.querySelector("[data-summary-field='body']").value.trim();
const values = { body };
SUMMARY_REQUIRED_FIELDS.forEach(([field]) => {
const input = item.querySelector(`[data-summary-field='${field}']`);
values[field] = input.value.trim() || inferSummaryField(body, field);
if (!input.value.trim() && values[field]) input.value = values[field];
});
const missing = SUMMARY_REQUIRED_FIELDS.filter(([field]) => !values[field]).map(([, label]) => label);
const missingNode = item.querySelector(".summary-register-missing");
missingNode.hidden = missing.length === 0;
missingNode.textContent = missing.length ? `请补齐:${missing.join("、")};无法补齐请删除本框,本条不会提交。` : "";
item.classList.toggle("has-missing", missing.length > 0);
if (!body || missing.length) return null;
return [
`学生:${values.student}`,
`日期:${values.date}`,
`时间:${values.time}`,
`老师:${values.teacher}`,
`科目:${values.subject}`,
"小结:",
body,
].join("\n");
}
function summaryRegisterLines() {
return Array.from(summaryRegisterItems.querySelectorAll("textarea"))
.map((textarea) => textarea.value.trim())
.filter(Boolean);
const items = Array.from(summaryRegisterItems.querySelectorAll(".summary-register-item"));
const lines = items.map(summaryRegisterItemPayload).filter(Boolean);
return {
lines,
blocked: items.some((item) => item.classList.contains("has-missing")),
};
}
async function submitSummaryRegister(event) {
event.preventDefault();
const lines = summaryRegisterLines();
const { lines, blocked } = summaryRegisterLines();
if (blocked) {
summaryRegisterStatus.textContent = "还有课程小结缺字段,请补齐后再提交;无法补齐的请删除本框。";
return;
}
if (!lines.length) {
summaryRegisterStatus.textContent = "请至少填写一条课程小结";
return;
@@ -621,7 +682,7 @@ async function submitSummaryRegister(event) {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ lines }),
});
summaryRegisterStatus.textContent = `已接收 ${data.received} 条;自动登记 ${data.auto_registered} 条;待审核 ${data.review_pending} 条;重复 ${data.duplicates} 条;失败 ${data.rejected}`;
summaryRegisterStatus.textContent = `已接收 ${data.received} 条;自动登记 ${data.auto_registered} 条;重复 ${data.duplicates} 条;舍弃 ${data.rejected}`;
summaryRegisterItems.innerHTML = "";
addSummaryRegisterItem();
await loadAdminHealth();
+25
View File
@@ -383,12 +383,33 @@ textarea:focus {
gap: 7px;
}
.summary-register-item.has-missing {
padding: 10px;
border: 1px solid #fecdca;
border-radius: 6px;
background: #fff7f6;
}
.summary-register-item label {
color: #344054;
font-size: 13px;
font-weight: 700;
}
.summary-register-fields {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.summary-register-missing {
margin: 0;
color: var(--danger);
font-size: 13px;
font-weight: 700;
line-height: 1.45;
}
.summary-register-remove {
justify-self: start;
}
@@ -1003,6 +1024,10 @@ td {
grid-template-columns: 1fr;
}
.summary-register-fields {
grid-template-columns: 1fr;
}
.admin-tabs {
overflow-x: auto;
}