课程库 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
@@ -28,6 +28,7 @@ public sealed class DatabaseInitializer(
await SeedRolesAsync();
await SeedAdministratorAsync();
await SeedCourseCategoriesAsync();
if (environment.IsDevelopment())
{
@@ -108,6 +109,55 @@ public sealed class DatabaseInitializer(
}
}
private async Task SeedCourseCategoriesAsync()
{
var defaults = new[]
{
("BASIC", "基础课程", 10),
("MORAL", "德育课程", 20),
("AESTHETIC", "美育课程", 30),
("LABOR", "劳动教育", 40),
("INNOVATION", "创新创业", 50),
("ENGLISH", "大学英语", 60),
("SPORTS", "大学体育", 70),
("MILITARY", "国防教育", 80),
("MAJOR", "专业教育", 90),
("PRACTICE", "实践教学", 100)
};
var existingCodes = (await db.CourseCategories
.Select(x => x.Code)
.ToListAsync())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var (code, name, sortOrder) in defaults)
{
if (existingCodes.Contains(code)) continue;
db.CourseCategories.Add(new CourseCategory
{
Code = code,
Name = name,
SortOrder = sortOrder
});
}
await db.SaveChangesAsync();
var categories = await db.CourseCategories
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase);
var uncategorizedCourses = await db.Courses
.Where(x => x.CourseCategoryId == null)
.ToListAsync();
foreach (var course in uncategorizedCourses)
{
var categoryCode = course.Nature switch
{
CourseNature.Practice => "PRACTICE",
CourseNature.MajorRequired or CourseNature.MajorElective => "MAJOR",
_ => "BASIC"
};
course.CourseCategoryId = categories[categoryCode].Id;
}
await db.SaveChangesAsync();
}
private async Task SeedDevelopmentDataAsync()
{
if (!await db.Campuses.AnyAsync())
@@ -242,6 +292,9 @@ public sealed class DatabaseInitializer(
});
}
var courseCategories = await db.CourseCategories
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase);
if (!await db.Courses.AnyAsync())
{
db.Courses.AddRange(
@@ -251,6 +304,7 @@ public sealed class DatabaseInitializer(
Name = "程序设计基础",
EnglishName = "Fundamentals of Programming",
CollegeId = computerCollege.Id,
CourseCategoryId = courseCategories["BASIC"].Id,
Credits = 4,
TotalHours = 64,
LectureHours = 40,
@@ -265,6 +319,7 @@ public sealed class DatabaseInitializer(
Name = "数据结构",
EnglishName = "Data Structures",
CollegeId = computerCollege.Id,
CourseCategoryId = courseCategories["BASIC"].Id,
Credits = 3.5m,
TotalHours = 56,
LectureHours = 40,
@@ -278,6 +333,7 @@ public sealed class DatabaseInitializer(
Name = "软件工程实践",
EnglishName = "Software Engineering Practice",
CollegeId = computerCollege.Id,
CourseCategoryId = courseCategories["PRACTICE"].Id,
Credits = 2,
TotalHours = 48,
LectureHours = 8,
@@ -286,7 +342,6 @@ public sealed class DatabaseInitializer(
AssessmentMethod = AssessmentMethod.Assessment
});
}
await db.SaveChangesAsync();
if (!await db.CurriculumPlans.AnyAsync())