优化课程小结查询操作展示
This commit is contained in:
@@ -2408,6 +2408,7 @@ def course_summary_index_for_records(root: Path) -> dict[tuple[str, str, str, st
|
|||||||
|
|
||||||
def query_course_summaries(
|
def query_course_summaries(
|
||||||
root: Path,
|
root: Path,
|
||||||
|
classnotes_path: Path | None = None,
|
||||||
q: str = "",
|
q: str = "",
|
||||||
student: str = "",
|
student: str = "",
|
||||||
teacher: str = "",
|
teacher: str = "",
|
||||||
@@ -2438,6 +2439,18 @@ def query_course_summaries(
|
|||||||
]
|
]
|
||||||
for item in matched:
|
for item in matched:
|
||||||
item["matched_fields"] = course_summary_matched_fields(item, keyword)
|
item["matched_fields"] = course_summary_matched_fields(item, keyword)
|
||||||
|
item["matched_record"] = False
|
||||||
|
if classnotes_path is not None and classnotes_path.exists():
|
||||||
|
record_keys = {
|
||||||
|
course_summary_record_key(record.student, record.teacher, record.subject, record.date.replace(".", "-"), record.time)
|
||||||
|
for record in read_classnotes(classnotes_path)
|
||||||
|
}
|
||||||
|
for item in matched:
|
||||||
|
try:
|
||||||
|
key = course_summary_duplicate_key(item)
|
||||||
|
except ValueError:
|
||||||
|
key = ("", "", "", "", "")
|
||||||
|
item["matched_record"] = bool(key and all(key) and key in record_keys)
|
||||||
matched.sort(
|
matched.sort(
|
||||||
key=lambda item: (
|
key=lambda item: (
|
||||||
str(item.get("date_iso") or "0000-00-00"),
|
str(item.get("date_iso") or "0000-00-00"),
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ def admin_course_summaries(
|
|||||||
try:
|
try:
|
||||||
return query_course_summaries(
|
return query_course_summaries(
|
||||||
COURSE_SUMMARIES_ROOT,
|
COURSE_SUMMARIES_ROOT,
|
||||||
|
CLASSNOTES_PATH,
|
||||||
q=q,
|
q=q,
|
||||||
student=student,
|
student=student,
|
||||||
teacher=teacher,
|
teacher=teacher,
|
||||||
|
|||||||
@@ -785,24 +785,24 @@ function summarySearchParams() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderSummaryActions(item) {
|
function renderSummaryActions(item) {
|
||||||
|
const editButton = `<button class="secondary-button summary-body-edit" type="button" data-summary-id="${escapeHtml(item.id)}">修改课程小结</button>`;
|
||||||
|
const deleteButton = `<button class="secondary-button summary-delete" type="button" data-summary-id="${escapeHtml(item.id)}">删除课程小结</button>`;
|
||||||
if (item.time_range) {
|
if (item.time_range) {
|
||||||
return `<span class="summary-action-note">时间已完整</span>`;
|
return `<div class="summary-time-actions"><span class="summary-action-note">时间已完整</span>${editButton}${deleteButton}</div>`;
|
||||||
}
|
}
|
||||||
const inputId = `summary-time-${item.id}`;
|
const inputId = `summary-time-${item.id}`;
|
||||||
return `<div class="summary-time-actions">
|
return `<div class="summary-time-actions">
|
||||||
<input id="${escapeHtml(inputId)}" class="summary-time-input" data-summary-time-input="${escapeHtml(item.id)}" autocomplete="off" placeholder="08:00-10:00" />
|
<input id="${escapeHtml(inputId)}" class="summary-time-input" data-summary-time-input="${escapeHtml(item.id)}" autocomplete="off" placeholder="08:00-10:00" />
|
||||||
<button class="secondary-button summary-time-save" type="button" data-summary-id="${escapeHtml(item.id)}">补齐时间</button>
|
<button class="secondary-button summary-time-save" type="button" data-summary-id="${escapeHtml(item.id)}">补齐时间</button>
|
||||||
<button class="secondary-button summary-delete" type="button" data-summary-id="${escapeHtml(item.id)}">删除课程小结</button>
|
${editButton}
|
||||||
|
${deleteButton}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSummaryBodyEditor(item) {
|
function renderSummaryBodyEditor(item) {
|
||||||
const body = item.body || item.body_preview || "";
|
const body = item.body || item.body_preview || "";
|
||||||
if (String(item.id) !== editingSummarySearchId) {
|
if (String(item.id) !== editingSummarySearchId) {
|
||||||
return `<div class="summary-body summary-search-full">${escapeHtml(body || "暂无正文")}</div>
|
return `<div class="summary-body summary-search-full">${escapeHtml(body || "暂无正文")}</div>`;
|
||||||
<div class="summary-edit-actions">
|
|
||||||
<button class="secondary-button summary-body-edit" type="button" data-summary-id="${escapeHtml(item.id)}">修改课程小结</button>
|
|
||||||
</div>`;
|
|
||||||
}
|
}
|
||||||
return `<div class="summary-body-edit-panel">
|
return `<div class="summary-body-edit-panel">
|
||||||
<textarea class="summary-body-editor" data-summary-body-editor="${escapeHtml(item.id)}" rows="10">${escapeHtml(body)}</textarea>
|
<textarea class="summary-body-editor" data-summary-body-editor="${escapeHtml(item.id)}" rows="10">${escapeHtml(body)}</textarea>
|
||||||
@@ -822,7 +822,8 @@ function renderSummarySearchRows(items) {
|
|||||||
return items
|
return items
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
const expanded = String(item.id) === expandedSummarySearchId;
|
const expanded = String(item.id) === expandedSummarySearchId;
|
||||||
const mainRow = `<tr class="summary-search-result-row" data-summary-id="${escapeHtml(item.id)}" tabindex="0" role="button" aria-expanded="${expanded ? "true" : "false"}">
|
const unmatchedClass = item.matched_record ? "" : " unmatched-summary";
|
||||||
|
const mainRow = `<tr class="summary-search-result-row${unmatchedClass}" data-summary-id="${escapeHtml(item.id)}" tabindex="0" role="button" aria-expanded="${expanded ? "true" : "false"}">
|
||||||
<td>${escapeHtml(item.date_iso || "未识别")}</td>
|
<td>${escapeHtml(item.date_iso || "未识别")}</td>
|
||||||
<td>${escapeHtml(item.time_range || "缺少时间")}</td>
|
<td>${escapeHtml(item.time_range || "缺少时间")}</td>
|
||||||
<td>${escapeHtml(item.student || "")}<br><small>${escapeHtml(item.group || "")}</small></td>
|
<td>${escapeHtml(item.student || "")}<br><small>${escapeHtml(item.group || "")}</small></td>
|
||||||
@@ -831,7 +832,7 @@ function renderSummarySearchRows(items) {
|
|||||||
const detailRow = expanded
|
const detailRow = expanded
|
||||||
? `<tr class="summary-search-detail-row" data-summary-detail="${escapeHtml(item.id)}">
|
? `<tr class="summary-search-detail-row" data-summary-detail="${escapeHtml(item.id)}">
|
||||||
<td colspan="4">
|
<td colspan="4">
|
||||||
<div class="summary-search-detail-panel">
|
<div class="summary-search-detail-panel${unmatchedClass}">
|
||||||
<div class="summary-search-detail-head">
|
<div class="summary-search-detail-head">
|
||||||
<div>
|
<div>
|
||||||
<div class="record-summary-title">${escapeHtml(item.title || "课程小结正文")}</div>
|
<div class="record-summary-title">${escapeHtml(item.title || "课程小结正文")}</div>
|
||||||
|
|||||||
@@ -726,16 +726,46 @@ textarea:focus {
|
|||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.summary-search-result-row.unmatched-summary td:first-child {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-search-result-row.unmatched-summary td:first-child::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 4px;
|
||||||
|
background: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
.summary-search-detail-row td {
|
.summary-search-detail-row td {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #fbfcfd;
|
background: #fbfcfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-search-detail-panel {
|
.summary-search-detail-panel {
|
||||||
|
position: relative;
|
||||||
padding: 14px 16px 16px;
|
padding: 14px 16px 16px;
|
||||||
border-top: 1px solid var(--line);
|
border-top: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.summary-search-detail-panel.unmatched-summary {
|
||||||
|
padding-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-search-detail-panel.unmatched-summary::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 4px;
|
||||||
|
background: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
.summary-search-detail-head {
|
.summary-search-detail-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -770,7 +800,7 @@ textarea:focus {
|
|||||||
.summary-search-actions {
|
.summary-search-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
@@ -779,7 +809,7 @@ textarea:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.summary-search-actions .summary-time-actions {
|
.summary-search-actions .summary-time-actions {
|
||||||
justify-content: flex-end;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-action-note {
|
.summary-action-note {
|
||||||
@@ -1717,7 +1747,7 @@ td {
|
|||||||
|
|
||||||
.summary-search-actions,
|
.summary-search-actions,
|
||||||
.summary-search-actions .summary-time-actions {
|
.summary-search-actions .summary-time-actions {
|
||||||
justify-content: stretch;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-search-actions .secondary-button,
|
.summary-search-actions .secondary-button,
|
||||||
|
|||||||
Reference in New Issue
Block a user