Add batch course arrangement to academic planning

This commit is contained in:
biss committed 2026-08-17 19:58:17 +08:00
1 parent 14bd01d2c3
commit 5dc2d45328
1 file changed
+82 -2
+82 -2
View File
@@ -18,6 +18,8 @@ const simulation = ref<any>(null)
const terms = ref<PlanningTerm[]>([])
const keyword = ref('')
const moduleFilter = ref('')
const selectedCourseIds = ref<string[]>([])
const batchSemester = ref<number>()
let simulationTimer: ReturnType<typeof setTimeout> | undefined
const courses = computed<any[]>(() => payload.value?.courses ?? [])
@@ -95,6 +97,29 @@ function removeCourse(courseId: string) {
setCourseSemester(courseId)
}
function setCourseSelected(courseId: string, selected: boolean) {
if (selected) {
if (!selectedCourseIds.value.includes(courseId)) {
selectedCourseIds.value.push(courseId)
}
return
}
selectedCourseIds.value = selectedCourseIds.value.filter((id) => id !== courseId)
}
function arrangeSelectedCourses() {
if (!batchSemester.value || !selectedCourseIds.value.length) return
const courseIds = selectedCourseIds.value.filter((courseId) =>
courses.value.some((course) => course.courseId === courseId),
)
for (const courseId of courseIds) {
setCourseSemester(courseId, batchSemester.value)
}
selectedCourseIds.value = []
const target = terms.value.find((term) => term.semester === batchSemester.value)
ElMessage.success(`已将 ${courseIds.length} 门课程安排到${target?.label ?? `第 ${batchSemester.value} 学期`}`)
}
function applySuggestion() {
const target = terms.value.find(
(term) => term.semester === payload.value?.plan?.nextSemester,
@@ -142,6 +167,8 @@ async function load() {
...term,
courseIds: [],
}))
selectedCourseIds.value = []
batchSemester.value = undefined
initialized.value = true
await simulate()
} catch (error) {
@@ -298,6 +325,32 @@ onBeforeUnmount(() => {
/>
</el-select>
</div>
<div class="batch-arrange-bar">
<el-checkbox
:model-value="remainingCourses.length > 0 && remainingCourses.every((course) => selectedCourseIds.includes(course.courseId))"
:indeterminate="selectedCourseIds.some((courseId) => remainingCourses.some((course) => course.courseId === courseId)) && !remainingCourses.every((course) => selectedCourseIds.includes(course.courseId))"
@change="(selected) => remainingCourses.forEach((course) => setCourseSelected(course.courseId, selected === true))"
>
全选当前结果(已选 {{ selectedCourseIds.length }} 门)
</el-checkbox>
<div>
<el-select v-model="batchSemester" placeholder="批量安排到学期">
<el-option
v-for="term in terms"
:key="term.semester"
:label="`第 ${term.semester} 学期 · ${term.label}`"
:value="term.semester"
/>
</el-select>
<el-button
type="primary"
:disabled="!batchSemester || !selectedCourseIds.length"
@click="arrangeSelectedCourses"
>
批量安排
</el-button>
</div>
</div>
<div class="pool-list">
<article
v-for="course in remainingCourses"
@@ -308,7 +361,13 @@ onBeforeUnmount(() => {
}"
>
<div class="course-manifest-line">
<span>{{ course.courseCode }} · {{ course.moduleName }}</span>
<el-checkbox
:model-value="selectedCourseIds.includes(course.courseId)"
:aria-label="`选择课程 ${course.courseName}`"
@change="(selected) => setCourseSelected(course.courseId, selected === true)"
>
{{ course.courseCode }} · {{ course.moduleName }}
</el-checkbox>
<i>{{ course.credits }} 学分</i>
</div>
<h4>{{ course.courseName }}</h4>
@@ -627,6 +686,20 @@ onBeforeUnmount(() => {
gap: 8px;
border-bottom: 1px solid var(--line);
}
.batch-arrange-bar {
padding: 10px 11px;
display: grid;
gap: 8px;
border-bottom: 1px solid var(--line);
background: #f7f9fc;
}
.batch-arrange-bar > div {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 8px;
}
.batch-arrange-bar .el-select { width: 100%; }
.batch-arrange-bar .el-checkbox { height: auto; color: #536176; font-size: 10px; }
.pool-list {
max-height: 680px;
overflow: auto;
@@ -644,7 +717,14 @@ onBeforeUnmount(() => {
justify-content: space-between;
gap: 8px;
}
.course-manifest-line span { color: var(--teal); font: 700 9px/1.2 Consolas, monospace; }
.course-manifest-line .el-checkbox { min-width: 0; height: auto; }
.course-manifest-line :deep(.el-checkbox__label) {
overflow: hidden;
color: var(--teal);
font: 700 9px/1.2 Consolas, monospace;
text-overflow: ellipsis;
white-space: nowrap;
}
.course-manifest-line i { color: var(--muted); font-size: 9px; font-style: normal; }
.pool-list h4 { margin: 7px 0; font-size: 13px; }
.course-flags { display: flex; flex-wrap: wrap; gap: 5px; }