+ setDraft((state) => ({ ...state, keyword: event.target.value }))} onPressEnter={applyFilters} />
+ {canManage && <> void handleImport(event.target.files?.[0])} />} loading={importing} onClick={() => fileRef.current?.click()}>Excel 导入>}当前筛选条件会原样用于 Excel 导出 · 共 {courses.data?.total ?? 0} 门{selectedIds.length ? ` · 已选 ${selectedIds.length} 门` : ''}
+ {courses.isError ? : rowKey="id" loading={courses.isLoading} rowSelection={rowSelection} dataSource={courses.data?.items} columns={columns} pagination={false} scroll={{ x: 1450 }} locale={{ emptyText: }} />}
+
+
+ setEditing(undefined)} onOk={() => form.submit()}> ({ value: item.id, label: item.name }))} /> item.isEnabled).map((item) => ({ value: item.id, label: item.name }))} /> ({ value, label }))} /> item.id !== editing?.id).map((item) => ({ value: item.id, label: `${item.code} · ${item.name}` }))} />
+ void applyArchive()} onCancel={() => setArchive((state) => ({ ...state, open: false }))}>符合条件 {eligible.length} / {archive.previews.length} 门。提交时会再次检查,受阻课程不执行。
{archive.busy && !archive.previews.length ? : archive.previews.map((item) => {item.name || '不可操作课程'}{item.error && {item.error}
}{!item.error && <>{item.isArchived ? '已归档' : '未归档'}{item.archiveReason ? ` · 原因:${item.archiveReason}` : ''}
{item.isArchived === archive.target && 已处于目标状态,无需重复操作。
}{item.blockers?.map((blocker, index) => {blocker.message}
)}{item.notices?.map((notice, index) => {notice.message}
)}>})}{!archive.results.length ? : archive.results.map((result) => )}
+ setHistory((state) => ({ ...state, open: false }))}>{history.busy ? : history.entries.length ? history.entries.map((entry) => {entry.isArchived ? '归档' : '恢复'} · {entry.actorName || entry.actorUserId}{new Date(entry.createdAt).toLocaleString('zh-CN', { hour12: false })}
{entry.reason}
) : }第 {history.page} 页
+
+}
diff --git a/web-react/src/pages/course-catalogue.css b/web-react/src/pages/course-catalogue.css
new file mode 100644
index 0000000..300378a
--- /dev/null
+++ b/web-react/src/pages/course-catalogue.css
@@ -0,0 +1 @@
+.course-catalogue-page{gap:16px}.catalogue-header-actions{display:flex;gap:8px}.catalogue-ledger{display:grid;grid-template-columns:180px 1fr auto;align-items:stretch;border:1px solid var(--line);background:#fff}.catalogue-ledger>div{padding:13px 18px;border-right:1px solid var(--line)}.catalogue-ledger span,.catalogue-ledger strong,.catalogue-ledger small{display:block}.catalogue-ledger span{color:var(--ink-muted);font-size:8px;text-transform:uppercase}.catalogue-ledger strong{margin:4px 0;color:var(--brand-deep);font-size:16px}.catalogue-ledger div:first-child strong{font:700 23px var(--font-data);color:var(--teal)}.catalogue-ledger small{color:var(--ink-muted);font-size:8px}.catalogue-ledger>.anticon{align-self:center;margin:0 24px;color:var(--teal);font-size:27px}.catalogue-card{min-width:0;padding:16px;border:1px solid var(--line);background:#fff}.catalogue-filters,.catalogue-toolbar,.catalogue-header-actions,.catalogue-actions{display:flex;align-items:center;flex-wrap:wrap;gap:7px}.catalogue-filters{margin-bottom:12px}.catalogue-filters>.ant-input-affix-wrapper{width:235px}.catalogue-filters>.ant-select{width:155px}.catalogue-toolbar{justify-content:flex-start;margin-bottom:12px}.catalogue-toolbar>span{color:var(--ink-muted);font-size:9px}.catalogue-code{font:700 9px var(--font-data);color:var(--brand-deep)}.catalogue-name{display:grid;gap:3px}.catalogue-name strong{color:var(--brand-deep)}.catalogue-name span{color:var(--ink-muted);font-size:8px}.cursor-pager{display:flex;align-items:center;justify-content:space-between;margin-top:15px;color:var(--ink-muted);font-size:9px}.cursor-pager>span>.ant-select{width:68px}.cursor-pager>div{display:flex;align-items:center;gap:10px}.catalogue-form-grid{display:grid;grid-template-columns:1fr 1fr;align-items:start;gap:12px}.catalogue-form-grid.three{grid-template-columns:repeat(3,1fr)}.catalogue-form-grid .ant-input-number{width:100%}.catalogue-form-grid>.ant-alert{margin-top:29px}.catalogue-archive{padding:11px 0;border-bottom:1px solid var(--line)}.catalogue-archive p{margin:5px 0;color:var(--ink-muted);line-height:1.55}.catalogue-archive .error{color:#b42318}.catalogue-reason{display:grid;gap:7px;margin-top:14px;font-weight:600}@media(max-width:850px){.catalogue-ledger{grid-template-columns:1fr 1fr}.catalogue-ledger>.anticon{display:none}.catalogue-filters{align-items:stretch;flex-direction:column}.catalogue-filters>*{width:100%!important}}@media(max-width:600px){.catalogue-header-actions,.catalogue-toolbar{align-items:stretch;flex-direction:column}.catalogue-header-actions>*,.catalogue-toolbar>*{width:100%!important}.catalogue-ledger{grid-template-columns:1fr}.catalogue-ledger>div{border-right:0;border-bottom:1px solid var(--line)}.catalogue-form-grid,.catalogue-form-grid.three{grid-template-columns:1fr}.catalogue-form-grid>.ant-alert{margin-top:0}.cursor-pager{align-items:stretch;flex-direction:column}.cursor-pager>div{justify-content:space-between}}