培养方案“学期视图”。

This commit is contained in:
2026-08-09 16:47:19 +08:00 Unverified
parent f246378d5b
commit ae24371d7f
2 changed files with 92 additions and 3 deletions
+69 -3
View File
@@ -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 () => {
<div class="module-toolbar">
<div>
<b>课程结构</b>
<span>指定必修须逐门通过英语体育等多选课程用组内选修修满模块最低学分即可</span>
<b>{{ detailView === 'structure' ? '课程结构' : '学期视图' }}</b>
<span>{{ detailView === 'structure'
? '指定必修须逐门通过;英语、体育等多选课程用“组内选修”,修满模块最低学分即可'
: '按建议修读学期展示课程安排,包含每学期的课程数量、学分和学时。' }}</span>
</div>
<div class="curriculum-view-actions">
<el-radio-group v-model="detailView" size="small" aria-label="培养方案展示方式">
<el-radio-button value="structure">课程结构</el-radio-button>
<el-radio-button value="semester">学期视图</el-radio-button>
</el-radio-group>
<el-button v-if="canEdit && detailView === 'structure'" :icon="Plus" @click="openModule()">新增模块</el-button>
</div>
<el-button v-if="canEdit" :icon="Plus" @click="openModule()">新增模块</el-button>
</div>
<template v-if="detailView === 'structure'">
<section v-for="module in selected.modules" :key="module.id" class="curriculum-module">
<header>
<div>
@@ -500,6 +538,34 @@ onMounted(async () => {
</el-table>
</section>
<el-empty v-if="selected.modules.length === 0" description="先建立课程模块,再添加课程" />
</template>
<section v-else class="semester-view" aria-label="课程学期安排">
<article v-for="group in semesterGroups" :key="group.semester" class="semester-card">
<header>
<div>
<span>SEMESTER {{ String(group.semester).padStart(2, '0') }}</span>
<h4> {{ group.semester }} 学期</h4>
</div>
<b>{{ group.courses.length }} </b>
</header>
<div class="semester-summary">
<span>{{ group.credits }} 学分</span>
<span>{{ group.totalHours }} 学时</span>
</div>
<ul v-if="group.courses.length" class="semester-courses">
<li v-for="course in group.courses" :key="course.id">
<div>
<span>{{ course.moduleName }}</span>
<b>{{ course.courseName }}</b>
<small>{{ course.courseCode }} · {{ courseTypeLabels[course.type] }}</small>
</div>
<strong>{{ course.credits }}<small>学分</small></strong>
</li>
</ul>
<p v-else class="semester-empty">本学期暂未安排课程</p>
</article>
</section>
</template>
</main>
</div>