课程库 Excel 导入导出已完成:

支持下载标准导入模板。
导出严格沿用当前关键词、学院、分类、课程性质和状态筛选。
以课程编码为唯一键:存在则更新,不存在则新增。
校验学院编码、课程分类、课程性质、学分学时及考核方式。
整批原子导入,任一行错误则全部不写入,并提示具体行号。
学院管理员仍只能维护本学院的专业课和实践课,无法借 Excel 越权。
界面入口沿用现有课程库工具栏样式。
This commit is contained in:
2026-07-24 18:56:25 +08:00 Unverified
parent 5a7a8d530b
commit d7cf3f9e76
24 changed files with 3710 additions and 32 deletions
+22 -7
View File
@@ -42,8 +42,8 @@ const statusLabels: Record<string, string> = {
Archived: '已归档',
}
const courseTypeLabels: Record<string, string> = {
Required: '必修',
Elective: '选修',
Required: '指定必修',
Elective: '组内选修',
}
const isCollegeAdmin = computed(() => auth.user?.roles.includes('CollegeAdmin') ?? false)
const availableMajors = computed(() => majors.value)
@@ -410,7 +410,7 @@ onMounted(async () => {
<div class="module-toolbar">
<div>
<b>课程结构</b>
<span>发布前模块学分合计须与方案总学分一致</span>
<span>指定必修须逐门通过英语体育等多选课程用组内选修修满模块最低学分即可</span>
</div>
<el-button v-if="isDraft" :icon="Plus" @click="openModule()">新增模块</el-button>
</div>
@@ -421,7 +421,12 @@ onMounted(async () => {
<span>{{ module.code }}</span>
<h4>{{ module.name }}</h4>
</div>
<p>最低 {{ module.requiredCredits }} 学分 · 已配置 {{ module.assignedCredits }} 学分</p>
<p>
最低 {{ module.requiredCredits }} 学分 · 已配置 {{ module.assignedCredits }} 学分
<template v-if="module.courses.some((course: any) => course.type === 'Elective')">
· 课程组选项
</template>
</p>
<div v-if="isDraft">
<el-button link type="primary" @click="openModule(module)">编辑</el-button>
<el-button link type="danger" @click="deleteModule(module)">删除</el-button>
@@ -436,6 +441,9 @@ onMounted(async () => {
<el-table-column label="类别" width="80">
<template #default="{ row }">{{ courseTypeLabels[row.type] }}</template>
</el-table-column>
<el-table-column label="课程分类" width="100">
<template #default="{ row }">{{ row.categoryName || '未分类' }}</template>
</el-table-column>
<el-table-column prop="credits" label="学分" width="70" />
<el-table-column prop="totalHours" label="学时" width="70" />
<el-table-column label="建议学期" width="90">
@@ -489,7 +497,10 @@ onMounted(async () => {
<el-form-item label="排序"><el-input-number v-model="moduleForm.sortOrder" :min="0" /></el-form-item>
</div>
<el-form-item label="模块名称" required><el-input v-model="moduleForm.name" /></el-form-item>
<el-form-item label="最低学分要求"><el-input-number v-model="moduleForm.requiredCredits" :min="0" :precision="1" :step="0.5" /></el-form-item>
<el-form-item label="最低学分要求">
<el-input-number v-model="moduleForm.requiredCredits" :min="0" :precision="1" :step="0.5" />
<span class="form-help">体育英语等课程组填写学生必须从组内修满的学分</span>
</el-form-item>
</el-form>
<template #footer><el-button @click="moduleDialog = false">取消</el-button><el-button type="primary" @click="saveModule">保存</el-button></template>
</el-dialog>
@@ -498,14 +509,18 @@ onMounted(async () => {
<el-form label-position="top">
<el-form-item label="课程" required>
<el-select v-model="courseForm.courseId" filterable>
<el-option v-for="item in courses" :key="item.id" :label="`${item.code} · ${item.name}`" :value="item.id" />
<el-option v-for="item in courses" :key="item.id" :label="`${item.code} · ${item.name}${item.categoryName ? ` · ${item.categoryName}` : ''}`" :value="item.id" />
</el-select>
</el-form-item>
<div class="form-grid">
<el-form-item label="修读类别"><el-select v-model="courseForm.type"><el-option v-for="(label, value) in courseTypeLabels" :key="value" :label="label" :value="value" /></el-select></el-form-item>
<el-form-item label="修读规则"><el-select v-model="courseForm.type"><el-option v-for="(label, value) in courseTypeLabels" :key="value" :label="label" :value="value" /></el-select></el-form-item>
<el-form-item label="建议学期"><el-input-number v-model="courseForm.recommendedSemester" :min="1" :max="selected?.schoolingYears * 2" /></el-form-item>
</div>
<el-form-item label="备注"><el-input v-model="courseForm.notes" type="textarea" :rows="2" /></el-form-item>
<div class="course-rule-help">
<b>指定必修</b><span>该门课程必须通过</span>
<b>组内选修</b><span>足球篮球英语分层等作为选项按模块最低学分验收</span>
</div>
</el-form>
<template #footer><el-button @click="courseDialog = false">取消</el-button><el-button type="primary" @click="saveCourse">保存</el-button></template>
</el-dialog>