Add batch course arrangement to academic planning
This commit is contained in:
1 parent
14bd01d2c3
commit
5dc2d45328
1 file changed
+82
-2
@@ -18,6 +18,8 @@ const simulation = ref<any>(null)
|
|||||||
const terms = ref<PlanningTerm[]>([])
|
const terms = ref<PlanningTerm[]>([])
|
||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const moduleFilter = ref('')
|
const moduleFilter = ref('')
|
||||||
|
const selectedCourseIds = ref<string[]>([])
|
||||||
|
const batchSemester = ref<number>()
|
||||||
let simulationTimer: ReturnType<typeof setTimeout> | undefined
|
let simulationTimer: ReturnType<typeof setTimeout> | undefined
|
||||||
|
|
||||||
const courses = computed<any[]>(() => payload.value?.courses ?? [])
|
const courses = computed<any[]>(() => payload.value?.courses ?? [])
|
||||||
@@ -95,6 +97,29 @@ function removeCourse(courseId: string) {
|
|||||||
setCourseSemester(courseId)
|
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() {
|
function applySuggestion() {
|
||||||
const target = terms.value.find(
|
const target = terms.value.find(
|
||||||
(term) => term.semester === payload.value?.plan?.nextSemester,
|
(term) => term.semester === payload.value?.plan?.nextSemester,
|
||||||
@@ -142,6 +167,8 @@ async function load() {
|
|||||||
...term,
|
...term,
|
||||||
courseIds: [],
|
courseIds: [],
|
||||||
}))
|
}))
|
||||||
|
selectedCourseIds.value = []
|
||||||
|
batchSemester.value = undefined
|
||||||
initialized.value = true
|
initialized.value = true
|
||||||
await simulate()
|
await simulate()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -298,6 +325,32 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</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">
|
<div class="pool-list">
|
||||||
<article
|
<article
|
||||||
v-for="course in remainingCourses"
|
v-for="course in remainingCourses"
|
||||||
@@ -308,7 +361,13 @@ onBeforeUnmount(() => {
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="course-manifest-line">
|
<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>
|
<i>{{ course.credits }} 学分</i>
|
||||||
</div>
|
</div>
|
||||||
<h4>{{ course.courseName }}</h4>
|
<h4>{{ course.courseName }}</h4>
|
||||||
@@ -627,6 +686,20 @@ onBeforeUnmount(() => {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
border-bottom: 1px solid var(--line);
|
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 {
|
.pool-list {
|
||||||
max-height: 680px;
|
max-height: 680px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -644,7 +717,14 @@ onBeforeUnmount(() => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
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; }
|
.course-manifest-line i { color: var(--muted); font-size: 9px; font-style: normal; }
|
||||||
.pool-list h4 { margin: 7px 0; font-size: 13px; }
|
.pool-list h4 { margin: 7px 0; font-size: 13px; }
|
||||||
.course-flags { display: flex; flex-wrap: wrap; gap: 5px; }
|
.course-flags { display: flex; flex-wrap: wrap; gap: 5px; }
|
||||||
|
|||||||
Reference in new issue
Block a user