Implement requested workflow and UI updates

This commit is contained in:
biss committed 2026-08-18 10:16:02 +08:00
1 parent 5959585864
commit 390d0b123f
18 files changed
+7800 -35

No files matched your search

+1
View File
@@ -137,6 +137,7 @@ const navigationGroups = computed<NavigationGroup[]>(() => [
},
),
...whenVisible(isTeachingAdmin.value, { path: '/curriculum', label: '培养方案' }),
...whenVisible(isTeachingAdmin.value, { path: '/course-equivalency-rules', label: '等价课程规则' }),
...whenVisible(isStudent.value, { path: '/my-curriculum', label: '我的培养方案' }),
...whenVisible(isTeachingAdmin.value, { path: '/teaching-tasks', label: '教学任务' }),
],
+6
View File
@@ -251,6 +251,12 @@ const router = createRouter({
roles: ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student'],
},
},
{
path: 'course-equivalency-rules',
name: 'course-equivalency-rules',
component: () => import('../views/CourseEquivalencyRulesView.vue'),
meta: { roles: ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'] },
},
{
path: 'course-enrollments',
name: 'course-enrollments',
@@ -0,0 +1,49 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import http, { apiErrorMessage } from '../api/http'
const rows = ref<any[]>([])
const courses = ref<any[]>([])
const form = reactive({ originalCourseId: '', substituteCourseId: '', effectiveGrade: undefined as number | undefined, notes: '' })
const loading = ref(false)
async function load() {
loading.value = true
try {
const [ruleResponse, courseResponse] = await Promise.all([
http.get('/course-equivalency-rules'),
http.get('/curriculum-plans/course-options'),
])
rows.value = ruleResponse.data
courses.value = courseResponse.data
} catch (error) { ElMessage.error(apiErrorMessage(error)) }
finally { loading.value = false }
}
async function create() {
if (!form.originalCourseId || !form.substituteCourseId) return ElMessage.warning('请选择被替代课程和等价课程')
try {
await http.post('/course-equivalency-rules', { ...form, isEnabled: true, majorId: null })
form.originalCourseId = ''; form.substituteCourseId = ''; form.effectiveGrade = undefined; form.notes = ''
ElMessage.success('等价课程规则已建立')
await load()
} catch (error) { ElMessage.error(apiErrorMessage(error)) }
}
async function toggle(row: any) {
try {
await http.put(`/course-equivalency-rules/${row.id}`, { originalCourseId: row.originalCourseId, substituteCourseId: row.substituteCourseId, majorId: row.majorId, effectiveGrade: row.effectiveGrade, notes: row.notes, isEnabled: !row.isEnabled })
await load()
} catch (error) { ElMessage.error(apiErrorMessage(error)) }
}
onMounted(load)
</script>
<template>
<div class="page-stack" v-loading="loading">
<section class="page-intro"><div><span class="section-kicker">EQUIVALENCY RULES</span><h2>等价课程规则</h2><p>先维护可追溯的等价关系;学生只能按适用规则申请课程替代。</p></div></section>
<el-card><el-form :inline="true" @submit.prevent="create"><el-form-item label="被替代课程"><el-select v-model="form.originalCourseId" filterable placeholder="选择课程" style="width:240px"><el-option v-for="course in courses" :key="course.id" :label="`${course.code} · ${course.name}`" :value="course.id" /></el-select></el-form-item><el-form-item label="等价课程"><el-select v-model="form.substituteCourseId" filterable placeholder="选择课程" style="width:240px"><el-option v-for="course in courses" :key="course.id" :label="`${course.code} · ${course.name}`" :value="course.id" /></el-select></el-form-item><el-form-item label="适用年级"><el-input-number v-model="form.effectiveGrade" :min="2000" :max="2100" controls-position="right" placeholder="全部" /></el-form-item><el-form-item label="说明"><el-input v-model="form.notes" maxlength="500" /></el-form-item><el-button type="primary" native-type="submit">建立规则</el-button></el-form></el-card>
<el-table :data="rows" stripe><el-table-column label="等价关系" min-width="300"><template #default="{ row }">{{ row.substituteCourseCode }} {{ row.substituteCourseName }} → {{ row.originalCourseCode }} {{ row.originalCourseName }}</template></el-table-column><el-table-column label="适用范围" min-width="160"><template #default="{ row }">{{ row.majorName || '全部专业' }} · {{ row.effectiveGrade || '全部年级' }}</template></el-table-column><el-table-column prop="notes" label="说明" min-width="180" /><el-table-column label="状态" width="130"><template #default="{ row }"><el-switch :model-value="row.isEnabled" active-text="启用" inactive-text="停用" @change="toggle(row)" /></template></el-table-column></el-table>
</div>
</template>