From ae24371d7f85e7e0e5b45fcc75facb89f1ae8a3b Mon Sep 17 00:00:00 2001 From: biss Date: Sun, 9 Aug 2026 16:47:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=B9=E5=85=BB=E6=96=B9=E6=A1=88=E2=80=9C?= =?UTF-8?q?=E5=AD=A6=E6=9C=9F=E8=A7=86=E5=9B=BE=E2=80=9D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/style.css | 23 ++++++++++ web/src/views/CurriculumView.vue | 72 ++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/web/src/style.css b/web/src/style.css index 351df9e..8ab55fb 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -431,6 +431,8 @@ button { cursor: pointer; } .module-toolbar b, .module-toolbar span { display: block; } .module-toolbar b { font-size: 15px; } .module-toolbar span { margin-top: 4px; color: var(--muted); font-size: 10px; } +.curriculum-view-actions { display: flex; align-items: center; gap: 10px; } +.curriculum-view-actions .el-button + .el-button { margin-left: 0; } .curriculum-module { margin-top: 12px; border: 1px solid var(--line); } .curriculum-module > header { min-height: 72px; padding: 13px 16px; display: flex; align-items: center; gap: 18px; background: #f8fafb; border-bottom: 1px solid var(--line); } .curriculum-module > header > div:first-child { min-width: 160px; } @@ -439,6 +441,24 @@ button { cursor: pointer; } .curriculum-module > header p { margin: 0 auto 0 0; color: var(--muted); font-size: 11px; } .curriculum-module > header > div:last-child { display: flex; align-items: center; white-space: nowrap; } .curriculum-course-table { min-height: 80px; } +.semester-view { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } +.semester-card { min-width: 0; border: 1px solid var(--line); background: #fff; } +.semester-card > header { min-height: 70px; padding: 14px 16px; display: flex; align-items: center; justify-content: space-between; background: linear-gradient(135deg, #f4faf9, #f8fafb); border-bottom: 1px solid var(--line); } +.semester-card > header span { color: var(--teal); font: 700 9px/1 Consolas, monospace; letter-spacing: .12em; } +.semester-card h4 { margin: 7px 0 0; font-size: 15px; } +.semester-card > header > b { color: #1d6b68; font: 700 18px/1 Consolas, monospace; } +.semester-summary { display: flex; gap: 14px; padding: 9px 16px; color: var(--muted); font-size: 11px; border-bottom: 1px solid var(--line); } +.semester-summary span + span { padding-left: 14px; border-left: 1px solid var(--line); } +.semester-courses { margin: 0; padding: 0; list-style: none; } +.semester-courses li { min-height: 68px; padding: 11px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid #edf0f2; } +.semester-courses li:last-child { border-bottom: none; } +.semester-courses li > div { min-width: 0; display: grid; gap: 3px; } +.semester-courses li span { color: var(--teal); font-size: 10px; } +.semester-courses li b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; } +.semester-courses li small { color: var(--muted); font-size: 10px; } +.semester-courses li > strong { flex: none; font: 700 15px/1 Consolas, monospace; } +.semester-courses li > strong small { margin-left: 3px; font: 400 9px/1 inherit; } +.semester-empty { margin: 0; padding: 25px 16px; color: var(--muted); font-size: 11px; text-align: center; } .task-summary { min-height: 82px; padding: 15px 22px; display: flex; align-items: center; gap: 28px; color: white; background: linear-gradient(108deg, #17295a, #263f80); } .task-summary > div { min-width: 170px; display: flex; align-items: baseline; gap: 8px; } .task-summary span, .task-summary small { color: #b8c1de; font-size: 10px; } @@ -1280,6 +1300,9 @@ button { cursor: pointer; } .plan-metrics > div:nth-child(-n+2) { border-bottom: 1px solid var(--line); } .curriculum-module > header { align-items: flex-start; flex-wrap: wrap; } .curriculum-module > header p { order: 3; width: 100%; } + .module-toolbar { align-items: flex-start; flex-direction: column; gap: 12px; } + .curriculum-view-actions { width: 100%; justify-content: space-between; } + .semester-view { grid-template-columns: 1fr; } .task-summary { align-items: flex-start; flex-direction: column; gap: 12px; } .task-summary > div { width: 100%; } .task-summary p { padding: 12px 0 0; border-left: none; border-top: 1px solid rgba(255,255,255,.16); line-height: 1.6; } diff --git a/web/src/views/CurriculumView.vue b/web/src/views/CurriculumView.vue index fcdcd1e..50c1c12 100644 --- a/web/src/views/CurriculumView.vue +++ b/web/src/views/CurriculumView.vue @@ -18,6 +18,7 @@ const planDialog = ref(false) const moduleDialog = ref(false) const courseDialog = ref(false) const cloneDialog = ref(false) +const detailView = ref<'structure' | 'semester'>('structure') const editingPlanId = ref('') const editingPlanStatus = ref('') const editingModuleId = ref('') @@ -74,6 +75,34 @@ const configuredCredits = computed(() => 0, ) ?? 0, ) +const semesterGroups = computed(() => { + if (!selected.value) return [] + + const courses = selected.value.modules.flatMap((module: any) => + module.courses.map((course: any) => ({ + ...course, + moduleCode: module.code, + moduleName: module.name, + })), + ) + const semesterCount = Number(selected.value.schoolingYears) * 2 + + return Array.from({ length: semesterCount }, (_, index) => { + const semester = index + 1 + const items = courses + .filter((course: any) => Number(course.recommendedSemester) === semester) + .sort((first: any, second: any) => + first.moduleCode.localeCompare(second.moduleCode) || + first.courseCode.localeCompare(second.courseCode), + ) + return { + semester, + courses: items, + credits: items.reduce((sum: number, course: any) => sum + Number(course.credits), 0), + totalHours: items.reduce((sum: number, course: any) => sum + Number(course.totalHours), 0), + } + }) +}) async function loadPlans(keepSelection = true) { loading.value = true @@ -450,12 +479,21 @@ onMounted(async () => {
- 课程结构 - 指定必修须逐门通过;英语、体育等多选课程用“组内选修”,修满模块最低学分即可 + {{ detailView === 'structure' ? '课程结构' : '学期视图' }} + {{ detailView === 'structure' + ? '指定必修须逐门通过;英语、体育等多选课程用“组内选修”,修满模块最低学分即可' + : '按建议修读学期展示课程安排,包含每学期的课程数量、学分和学时。' }} +
+
+ + 课程结构 + 学期视图 + + 新增模块
- 新增模块
+ + +
+
+
+
+ SEMESTER {{ String(group.semester).padStart(2, '0') }} +

第 {{ group.semester }} 学期

+
+ {{ group.courses.length }} 门 +
+
+ {{ group.credits }} 学分 + {{ group.totalHours }} 学时 +
+
    +
  • +
    + {{ course.moduleName }} + {{ course.courseName }} + {{ course.courseCode }} · {{ courseTypeLabels[course.type] }} +
    + {{ course.credits }}学分 +
  • +
+

本学期暂未安排课程

+
+